{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:25.356538Z",
     "start_time": "2025-01-17T01:58:25.352155Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载数据"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:25.399767Z",
     "start_time": "2025-01-17T01:58:25.368588Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
    "# 从数据集到dataloader\n",
    "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
    "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:25.403025Z",
     "start_time": "2025-01-17T01:58:25.399767Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2860], [0.3205])\n",
    ")\n"
   ],
   "outputs": [],
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
    "\n",
    "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
    "\n",
    "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
    "|-|-|-|-|-|\n",
    "|20|默认|\n",
    "|20|xaviier_uniform|\n",
    "|20|he_uniform|\n",
    "|...|\n",
    "\n",
    "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:25.413218Z",
     "start_time": "2025-01-17T01:58:25.403025Z"
    }
   },
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, layers_num=2):\n",
    "        super().__init__()\n",
    "        self.transforms = transforms\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 多加几层\n",
    "        self.linear_relu_stack = nn.Sequential(\n",
    "            nn.Linear(28 * 28, 100),  # in_features=784, out_features=300\n",
    "            # 一般而言，先batch norm 后 激活函数\n",
    "            # 参见论文 《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》\n",
    "            nn.BatchNorm1d(100),  # num of features=100\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        # 加19层\n",
    "        for i in range(1, layers_num):\n",
    "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
    "            self.linear_relu_stack.add_module(f\"batchnorm_{i}\", nn.BatchNorm1d(100)) #因为特征是1维的，所以用1维的batchnorm\n",
    "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
    "        # 输出层\n",
    "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Linear):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 1, 28, 28]\n",
    "        x = self.transforms(x)\n",
    "        x = self.flatten(x)  \n",
    "        # 展平后 x.shape [batch size, 28 * 28]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 10]\n",
    "        return logits\n",
    "\n",
    "print(f\"{'layer_name':^40}\\tparamerters num\")\n",
    "total_params = 0\n",
    "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
    "    print(\"{}{:<40}\\t{:^10}\".format(idx,key, np.prod(value.shape)))\n",
    "    total_params += np.prod(value.shape)\n",
    "total_params"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "               layer_name               \tparamerters num\n",
      "0linear_relu_stack.0.weight              \t  78400   \n",
      "1linear_relu_stack.0.bias                \t   100    \n",
      "2linear_relu_stack.1.weight              \t   100    \n",
      "3linear_relu_stack.1.bias                \t   100    \n",
      "4linear_relu_stack.Linear_1.weight       \t  10000   \n",
      "5linear_relu_stack.Linear_1.bias         \t   100    \n",
      "6linear_relu_stack.batchnorm_1.weight    \t   100    \n",
      "7linear_relu_stack.batchnorm_1.bias      \t   100    \n",
      "8linear_relu_stack.Linear_2.weight       \t  10000   \n",
      "9linear_relu_stack.Linear_2.bias         \t   100    \n",
      "10linear_relu_stack.batchnorm_2.weight    \t   100    \n",
      "11linear_relu_stack.batchnorm_2.bias      \t   100    \n",
      "12linear_relu_stack.Linear_3.weight       \t  10000   \n",
      "13linear_relu_stack.Linear_3.bias         \t   100    \n",
      "14linear_relu_stack.batchnorm_3.weight    \t   100    \n",
      "15linear_relu_stack.batchnorm_3.bias      \t   100    \n",
      "16linear_relu_stack.Linear_4.weight       \t  10000   \n",
      "17linear_relu_stack.Linear_4.bias         \t   100    \n",
      "18linear_relu_stack.batchnorm_4.weight    \t   100    \n",
      "19linear_relu_stack.batchnorm_4.bias      \t   100    \n",
      "20linear_relu_stack.Linear_5.weight       \t  10000   \n",
      "21linear_relu_stack.Linear_5.bias         \t   100    \n",
      "22linear_relu_stack.batchnorm_5.weight    \t   100    \n",
      "23linear_relu_stack.batchnorm_5.bias      \t   100    \n",
      "24linear_relu_stack.Linear_6.weight       \t  10000   \n",
      "25linear_relu_stack.Linear_6.bias         \t   100    \n",
      "26linear_relu_stack.batchnorm_6.weight    \t   100    \n",
      "27linear_relu_stack.batchnorm_6.bias      \t   100    \n",
      "28linear_relu_stack.Linear_7.weight       \t  10000   \n",
      "29linear_relu_stack.Linear_7.bias         \t   100    \n",
      "30linear_relu_stack.batchnorm_7.weight    \t   100    \n",
      "31linear_relu_stack.batchnorm_7.bias      \t   100    \n",
      "32linear_relu_stack.Linear_8.weight       \t  10000   \n",
      "33linear_relu_stack.Linear_8.bias         \t   100    \n",
      "34linear_relu_stack.batchnorm_8.weight    \t   100    \n",
      "35linear_relu_stack.batchnorm_8.bias      \t   100    \n",
      "36linear_relu_stack.Linear_9.weight       \t  10000   \n",
      "37linear_relu_stack.Linear_9.bias         \t   100    \n",
      "38linear_relu_stack.batchnorm_9.weight    \t   100    \n",
      "39linear_relu_stack.batchnorm_9.bias      \t   100    \n",
      "40linear_relu_stack.Linear_10.weight      \t  10000   \n",
      "41linear_relu_stack.Linear_10.bias        \t   100    \n",
      "42linear_relu_stack.batchnorm_10.weight   \t   100    \n",
      "43linear_relu_stack.batchnorm_10.bias     \t   100    \n",
      "44linear_relu_stack.Linear_11.weight      \t  10000   \n",
      "45linear_relu_stack.Linear_11.bias        \t   100    \n",
      "46linear_relu_stack.batchnorm_11.weight   \t   100    \n",
      "47linear_relu_stack.batchnorm_11.bias     \t   100    \n",
      "48linear_relu_stack.Linear_12.weight      \t  10000   \n",
      "49linear_relu_stack.Linear_12.bias        \t   100    \n",
      "50linear_relu_stack.batchnorm_12.weight   \t   100    \n",
      "51linear_relu_stack.batchnorm_12.bias     \t   100    \n",
      "52linear_relu_stack.Linear_13.weight      \t  10000   \n",
      "53linear_relu_stack.Linear_13.bias        \t   100    \n",
      "54linear_relu_stack.batchnorm_13.weight   \t   100    \n",
      "55linear_relu_stack.batchnorm_13.bias     \t   100    \n",
      "56linear_relu_stack.Linear_14.weight      \t  10000   \n",
      "57linear_relu_stack.Linear_14.bias        \t   100    \n",
      "58linear_relu_stack.batchnorm_14.weight   \t   100    \n",
      "59linear_relu_stack.batchnorm_14.bias     \t   100    \n",
      "60linear_relu_stack.Linear_15.weight      \t  10000   \n",
      "61linear_relu_stack.Linear_15.bias        \t   100    \n",
      "62linear_relu_stack.batchnorm_15.weight   \t   100    \n",
      "63linear_relu_stack.batchnorm_15.bias     \t   100    \n",
      "64linear_relu_stack.Linear_16.weight      \t  10000   \n",
      "65linear_relu_stack.Linear_16.bias        \t   100    \n",
      "66linear_relu_stack.batchnorm_16.weight   \t   100    \n",
      "67linear_relu_stack.batchnorm_16.bias     \t   100    \n",
      "68linear_relu_stack.Linear_17.weight      \t  10000   \n",
      "69linear_relu_stack.Linear_17.bias        \t   100    \n",
      "70linear_relu_stack.batchnorm_17.weight   \t   100    \n",
      "71linear_relu_stack.batchnorm_17.bias     \t   100    \n",
      "72linear_relu_stack.Linear_18.weight      \t  10000   \n",
      "73linear_relu_stack.Linear_18.bias        \t   100    \n",
      "74linear_relu_stack.batchnorm_18.weight   \t   100    \n",
      "75linear_relu_stack.batchnorm_18.bias     \t   100    \n",
      "76linear_relu_stack.Linear_19.weight      \t  10000   \n",
      "77linear_relu_stack.Linear_19.bias        \t   100    \n",
      "78linear_relu_stack.batchnorm_19.weight   \t   100    \n",
      "79linear_relu_stack.batchnorm_19.bias     \t   100    \n",
      "80linear_relu_stack.Output Layer.weight   \t   1000   \n",
      "81linear_relu_stack.Output Layer.bias     \t    10    \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "275410"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 8
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:25.452431Z",
     "start_time": "2025-01-17T01:58:25.413218Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:27.747687Z",
     "start_time": "2025-01-17T01:58:25.452431Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:27.752691Z",
     "start_time": "2025-01-17T01:58:27.748692Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.makedirs(self.save_dir) #makedirs 创建多层目录\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:27.762393Z",
     "start_time": "2025-01-17T01:58:27.753194Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:27.769593Z",
     "start_time": "2025-01-17T01:58:27.762935Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "\n",
    "# 别 20 层了 2333，太深了没法训练\n",
    "model = NeuralNetwork(layers_num=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n"
   ],
   "outputs": [],
   "execution_count": 13
  },
  {
   "cell_type": "code",
   "source": [
    "loss_fct = nn.CrossEntropyLoss()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-17T01:58:27.771727Z",
     "start_time": "2025-01-17T01:58:27.769593Z"
    }
   },
   "outputs": [],
   "execution_count": 14
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/bn\")\n",
    "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
    "# 2. save best\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/bn\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
    "\n",
    "model = model.to(device)"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-17T02:17:52.703601Z",
     "start_time": "2025-01-17T01:58:27.771727Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/375000 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "3a710c21275e4b06b2fadc299704f8a8"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 41 / global_step 153750\n"
     ]
    }
   ],
   "execution_count": 15
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T02:17:52.868900Z",
     "start_time": "2025-01-17T02:17:52.703601Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+wAAAHACAYAAAA4KOLsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAtSBJREFUeJzs3Qd4U/X6B/Bv06R7AQVaZtl7b5ChLEXBLSIKorjxqqjXcV248N6roF4HLsSF4ta/ILIERPbee5RRyuxeaZv/8/5OTkgnHWlzknw/z3NMmibpOWklec/7/t7Xz2az2UBEREREREREhmJy9w4QERERERERUVEM2ImIiIiIiIgMiAE7ERERERERkQExYCciIiIiIiIyIAbsRERERERERAbEgJ2IiIiIiIjIgBiwExERERERERkQA3YiIiIiIiIiAzLDA+Tn5+PEiRMIDw+Hn5+fu3eHiIh8nM1mQ2pqKurVqweTiee+XYHv9UREZDQ2A7zfe0TALm/gDRs2dPduEBERFXD06FE0aNDA3bvhFfheT0RERnXUje/3HhGwy9l2/YWKiIio1HNZrVYsWLAAw4YNg8VigaficRgLj8NYeBzG4o3HkZmZqYJL/f2JKo/v9UXxOIyFx2EsPA5j8dbjSElJcfv7vUcE7HppnLyBu+JNPCQkRD2Pp/8x8TiMg8dhLDwOY/Hm42Dptuvwvb4oHoex8DiMhcdhLN5+HH5ufL/nwjsiIiIiIiIiA2LATkRERERERGRADNiJiIiIiIiIDMgj1rATEXnaCJDc3Fzk5eVVeh2V2WxGVlZWpZ/LnTz1OPz9/dV+c506ERERuQsDdiIiF8rJyUFCQgIyMjJcEvjHxMSortmeHDR68nFI45nY2FgEBAS4e1eIiIjIBzFgJyJykfz8fBw6dEhlZuvVq6eCvMoEqPJ8aWlpCAsLg8nkuSuYPPE45CSDnHw5ffq0+p22aNHC3btEREREPogBOxGRi0iAJ8GpzOuUzGxlyXPJcwYFBXlMoOtNxxEcHKxGuhw5ckTtv5yIISIiIqpOnvPJiYjIQ3hSUEql4++SiIiI3ImfRIiIiIiIiIgMiAE7ERERERERkQExYCciIpeKi4vDm2++6ZLnWrp0qWrcl5SU5JLno7JZvnw5Ro4cqZonyuv/888/l+l31bVrVwQGBqJ58+aYNWtWtewrERGRN2PATkREGDRoEB5++GGXPNe6detw9913u+S5yD3S09PRqVMnvPvuu2W6v3TSv/LKK3HppZdi8+bN6m9p4sSJ+OOPP6p8X4mIiLwZu8QTEVGZxpzl5eXBbL7420bt2rWrZZ+o6lxxxRVqK6sZM2agSZMmeOONN9TXbdq0wYoVKzB9+nQMHz68CveUiIh8RUZOLoIt/pUameuJfCpgP5GUiXu+WI/zSf4YMcLde0NEvhLoZlrzKjwOLTMnD+ac3Ap1Ky/rm9rtt9+OZcuWqe2tt95St3366aeYMGEC5s2bh2eeeQbbtm3DggUL1Mi6yZMnY/Xq1SoLK4HZ1KlTMWTIkAIl8ZJh1TP2Mg5NnnfJkiXqOerXr68Cu1GjRqEifvjhBzz33HPYv38/YmNj8eCDD+LRRx91fP+9995TgeLRo0cRGRmJ/v374/vvv1ffk8spU6aox8rovS5duuCXX35BaGhohfaFNKtWrSrwNyAkUC+taiM7O1ttupSUFHVptVrVVhn64yv7PKX5YeNxrDxwDi9d3QYhAVXzcao6jqM6/g18Z8l+LN1jQqfeaahfMwyeyht+H4LHYSw8jrKbPGcLTqdm4/mr2qBNbHi1HIcRfi/leod5//331Xb48GH1dbt27dSHptLOwn/33Xd49tln1WNatGiBf//73xjhpmjZ4m/CtuMpkI+vefk2WNyyF0TkSyRYb/uce8qCd744vEyBhATTe/fuRfv27fHiiy+q23bs2KEun3zySbz++uto2rQpatSooYJg+Tf8lVdeUWuVP//8c7XWec+ePWjUqFGJP0P+7f/Pf/6jnut///sfxo4dq+ab16xZs1zHtGHDBtx000144YUXMHr0aKxcuRL3338/atWqpU48rF+/Hv/4xz/wxRdfoG/fvjh37hz++usv9diEhASMGTNG7ce1116L1NRU9T0JKKhyTp48ibp16xa4Tb6WIDwzM1PNtC9MTvTIyZPC5KSOnExxhYULF6KqvL7RH2ey/RCUdgz96lbt31BVHkdVyrcB3x00YeUpOeFowg3v/Y0H2uWhZiA8mqf+PgrjcRgLj6N0x9KB+TvM8IMNf//9Fw655m3ioseRkZEBjwrYGzRogNdee00F3vIB57PPPsPVV1+NTZs2qeC9MPkgJR+O5E35qquuwuzZs3HNNddg48aN6oNhdYsM1kJ0G/yQmpWLoMCAat8HIiKjkSx0QECACpJiYmLUbbt371aXEsAPHTrUcV8JsGVts+6ll17CTz/9hF9//RWTJk0q8Wfccsst6v1AKgVeffVVvP3221i7di0uv/zycu3rtGnTMHjwYHUiWLRs2RI7d+7Ef//7XxWwx8fHq2y5vOeEh4ejcePGKouuB+y5ubm47rrr1O2iQ4cO5fr55DpPPfWUqtbQSXAvFRzDhg1DREREpZ5bMiLyYUv+di2Wqjk9/6+NSwDk4ogtGq+M6FElP6M6jqOq5Obl46mfdmDlqQSY/IAws02d4PjwQBi+mNAdjWtV8aftKuDJvw9nPA5j4XGUzcQvNgI4g6s6xmLiDR1RXcehV395TMAuWRRnkmGRjLuURhYXsEvWRj6MPf74444PdvICvPPOO2q9W3ULMJsQGuiP9Ow8JGXmoHak571ZEJFnkbJ0yXRXtCQ+NSUV4RHhFS6Jr6zu3bsX+DotLU1lt+fOnesIgCWDKoFyaZzfIySgloDs1KlT5d6fXbt2qRPFzvr166e60ssae3mDlWBcKgLk/Uc2yabLyQg50SDBvgTpUq4tgeENN9ygKgeocuRET2JiYoHb5Gv5PReXXRdSoSFbYfIByVUf9lz5XM6seflIy85V19cePo+zGXmIiQxCVamq46gqObn5ePSHrZi37ST8TX54/fr2SDmwCZ/FR+DgmQzc8sk6fDWxF1rUrZqS1qrmab+PkvA4jIXHUbL1h89h2d4z6t+TR4e1rpbXST8OI/xOKrzoSj4YSbm7rGHs06dPiWvanM+eC/mQdLHxMFW5ri0yyKIC9jMpmYir5blrFo20rqIyeBzGwuOo/M+V6iMJtGXTBZkrNpDDZvNDboB/hRusyL6Up9xb33ehX0qw5XwsslZ80aJFqqxcRnfJ96VEXf7Ndr6f83MJecNzvk2OR4J95/sUx3l/9OuFn9v5PnIyQMriZcSYnCCWZVtygmHNmjWIiopSXcul+ku+J6X5//rXv9R7lTRMK+nny8+T363+c1zxXuRt5HOA9DtwJq9xSZ8PPF1SxoXfv/wv9tvWE5jYv6lb98kosqx5mDR7IxbtOoUAfxP+d0sXXNayFuYd24Sv7uyBCZ9txO6TqRj94Wp8cWdPtKsX6e5dJiIDs9ls+O8fe9T1m7o3QFy058Zv1RawS+MheQPOyspCWFiYKoVs27Ztuda0ye2lqcp1baZcyTj5YenKdTi1y/PXLXK9i7HwOHz7OKSDumQaJQudk5PjsueVtdZVTTL4kinXT5Dqa7bkZztn92XN980336wy1UKOVUZ6yfuC/lgJbOU9onAZmfNxyBtwcfcprPB+NGvWTM0Id37cn3/+qW6XE8i6nj17qk2ankkTPKkI0KvEJMMu20MPPYSOHTvim2++wQMPPFDsz5ffo7wu8jPlBIP+d2WENW1VSX6v0phPJ79jGdcmSyKkV4GUsx8/flz1MBD33nuvqp775z//iTvuuEM1GPz222/V6+6NkjIK/v/9f1sYsAtpknn3F+vx174zCDSb8MFt3TCoVR3HCa7osEB8c3dvjJu5FluPJWPMh6vx+Z290LlhlLt3nYgMasX+M1hz6Jw6AfjgZS3gi8odsLdq1Uq9aScnJ6tuu+PHj1edhUsK2o22ru3rk+tw7NB5NG3THiO6NoSn4noXY+FxGIu7jkMCUGnKJiczg4IqXx4rQa0EqrIWu6pHmEjAK/+2S5M25/2Xn+387668B0gm9frrr1f7JBls2U9ZA6/fTwJreXzhf6+dj0Mui7tPYfpJWn0/nnjiCfTq1UutgZfMvmTHP/74YxUsyvd/++03FVxKZ3gpdZd9lRMInTt3VuX0EkjK30WdOnVU1v3MmTPqeyXth/xOpYpgwIABqtu9/nclQbw3kyoFmamu09+T5T1/1qxZajmE8zIIqVCQ4PyRRx5Ry+Gk5438Xrx1pNt5e4a9RogFKVm52HIsGYfPpPtk5kcnSwTu+HQd1h4+h5AAf3w8vjv6Nosucr+okAB8ObEXJny6DhuOnMetH6/BzNt7oGeT8jWgJCLvJ58vXrdn18f2boR6UcUvsfJ25Q7Y5UOZlEGKbt26Yd26derN+YMPPijzmja9qVFJqnJdW81QrdFcana+RwckOqOsragsHoex8DgqvlRIAlEJWCuy5rww5/JxVzxfaaTXiARj0hBUglEZ6yYKH4uMS5MM6iWXXILo6GgVQMtJhcL7WNw+F76tLK+T/n39vrKmXjK3cqLg5ZdfVmPdpDGe7JOQDLA0ppMqLQm2pUnq119/rTLqErBLhYC8Z8mJYFnrLuPlrrzyylJ/vuy3/B1JwC7kup5t91aDBg0qdTmFBO3FPUaa0PqC8/YMe6NaoYgIMquMspTFT/LR7E9yhhXjP12LzUeTEB5oxqw7eqBb45ID8IggCz6/oycmfrYeqw6exfiZa/HRuO64pEXRAJ+IfNfCnYnqhKgsDbx/kBZ/+iKzKz5QOq83dyYlkosXLy4wh9Xda9r0TvHJmVx/SESkk27rkq12Jl3XC5PycslSOytcTq6P/nQ+kVG49D0pKanCgaNk92UrjpxIkPXrxZGZ8fPnzy/TzyUqS0m8ZNiv7BCrAvZft/hmwH42LRu3fbIWOxNSEBViwRd39EKHBhdflx4aaManE3rg3i83YOme07jjs3WYcWtXXNa64FJKIvJN+fk2TFu4V12f0C8OtcM9fB5kJZjKW6ou6/jkw5isZZev5YORzNMV48aNU7fpZH2gfDiSDIaMCJLGP1JmV9ron6oWZQ/YkzK9OztCREREVV0SH4Bh7WLU2sq9iWnYfdL943+q06mULNz84WoVrEeHBaj16WUJ1nVBFn+1zn14u7qqs/zdn2/AvG0JVbrPROQZ/m/rCdWgMjzIjHsGNIMvK1fALiN4JCiXNYzScEjK4aXjrj6jV9azybo2Xd++fdXs9Q8//FCN05E179Ih3h0z2HVy9re4hjFERFT9pFmZrJkvbpPvERm5JF4CdqncG9Sqtvr6180n4CuOJ2Xipg9WYd+pNMREBGHOPX3QOqb8fYYCzf5455auGNWpHnLzbarD/E+bjlXJPhORZ5DRmdPt2fV7BjRFpD1+81XlKon/5JNPSv1+cWWIN954o9qMgiXxRETGIevPH3vssWK/V9kmo0RVJSn9QtM5MapzPSzYmagyQo8Pb1XlTSLdLf5sBsZ8tFoF7Q1qBGP2xN5oVKviU3ws/iZMH90ZQRYTvl1/DJO/3YJsaz5u7tnIpftNRJ7hhw3HcPhsBmqFBmBCv+LHrvqSSq9h9zQXSuIZsBMRuZt0a5eNyBMz7FH2RraDW9dVndGPnstUjde6NKoBb7X/VBrGfrwaiSnZaBIdiq8m9nJJ52Z/kx9eu66jKpP/fNURPPnjNmRa8/hhncjHZOfm4e3F+9T1+wY1U/0ufF3Vth02IL2kIjmDa9iJiIio/JKcxrqJ4AB/DG2rNUuT5nPealdCCkZ/sEoF6y3rhmHOPb1dOmbJZPLDlFHtcPcAbab9lP/bifeXHnDZ8xOR8c1eE48TyVlqqc2tvRu7e3cMweS7GXauYSciIqLKrWHXyRps8dvWBOTllzwSz1NtPZakyuDPpuegXb0IfHN3H9QJD3L5z5HlBE9d0Rr/GKx13P/3/N2qU3RpYwaJyDtk5OTi3T/3q+sPDm6uKm7IFwN2+9nwlKxcr3xDJSIiourpEq9/phD9W9RWfXJOp2ZjzaGz8CbrD5/D2I/WqMqCLo2iMPuu3qhpXw5QFSRonzy0JZ64vLX6Wspjp/6+m0E7kZebtfIwzqTloFHNENzUvaG7d8cwfC5gjwjS3lzl3/zULK5jJyIiorKToPHCHPYLQWuA2YQRHWLU9f/zorL4lfvPqDnrqdm56NWkJr64s5ejgW9Vk/WrL4xsq65/uPwgnvtlh5rNTETeRxqCf7DsoLr+yNAWqhklaXzulZA31EB/W4Ez5ERERERlkZadq8aPFQ7YxciOWln8vG0n1VxxT/fnnlOYMGudav7Wv0U0Zk3oibBqbgB1e78mmHpdB0jj/S9WSzO6rayQJPJCn/x1UAXtLeqEYVSn+u7eHUPxuYBdhNrfaziLnYjINeLi4vDmm2+Wudz1559/rvJ9IqrKhnOBZpNqNuesV9NaqBMeqD50/rXvNDzZ/O0ncffn65Gdm48hberi4/HdixxvdRnTsxGm3dRJdZKXsW+PzNms5jQTkXc4m5aNT1YcUtcfHdZS/b9OPh6wh+gBO0e7ERERUSUbzunkQ+aVHWM9vlv8L5uP44HZG2HNs6njef/Wrgg0u7f507VdGuCdMV1gNvmp13bS7I1q/BMReT6ZBpGek4cO9SMxvJ22tIjg6wG7VkqVzJJ4IiIiqmTDOWd6t/iFOxORmeN5AeW3647i4TmbVdn59V0b4O2buxhmLekVHWLx4bhuannjHzsScc8XG5Bl9bzXmIguOJmchc9XH3Fk16UKjwoyxr/AbiqJ18+SExFVGelwmZNe8c2aUfHHlrGj8ocffoh69eohP79gienVV1+NO+64AwcOHFDX69ati7CwMPTo0QOLFi1y2Uu0bds2XHbZZQgODkatWrVw9913Iy0tzfH9pUuXomfPnggNDUVUVBT69euHI0e0N/ctW7bg0ksvRXh4OCIiItCtWzesX7/eZftGVFhxDeecdW4YhYY1g5GRk4fFuxPhST5fdRj//GGr+qdjbK9G+O8NHQ1XmnpZ67qYOb4Hgi3+WLrnNCZ8ug7p2bnu3i0iqqD/Ldmnen70iKuBgS1ru3t3DKl6O4cYrSSeGXYiqmoScL+qZdwqckY1qjI/++kTQEDoRe9244034sEHH8Sff/6JwYMHq9vOnTuH+fPnY968eSp4HjFiBF555RUEBgbi888/x8iRI7Fnzx40atSoMnuI9PR0DB8+HH369MG6detw6tQpTJw4EZMmTcKsWbOQm5uLa665BnfddRe+/vpr5OTkYO3atY4z8GPHjkWXLl3w/vvvw9/fH5s3b4bFUj0drMk3nU+3B+yhxf+dyd+mNJ97b+kB/Lr5BK6yN6Izug+XH8Cr83ar63de0gTPXNnGsJmuS1pE47M7euKOWeuw6uBZjJu5Fp9O6OGYBEREniH+bAbmrDuqrj8+vLVh/81xN5MvB+zSFIaIyNfVqFEDV1xxBWbPnu247fvvv0d0dLTKXnfq1An33HMP2rdvjxYtWuCll15Cs2bN8Ouvv1b6Z8vPzMrKUicB5Pkl0/7OO+/giy++QGJiIlJSUpCcnIyrrrpK/cw2bdpg/PjxjhMF8fHxGDJkCFq3bq32TU4+yP4SVX1JfMlzyEd11oJ0yQAb/bOGjKl7a9E+R7A+6dLmhg7WdT2b1MSXE3shIsiMDUfOqznx+skUIvIMby7eq6ZuDGhZW/0/TcXzyQx7qH0NO0viiajKWUK0THcFSIl6SmoqIsLDYTKZKvazy0gy1ZLFfu+991QW/auvvsLNN9+sfq5k2F944QXMnTsXCQkJKuudmZmpguXK2rVrlwqwpdxdJyXvcuySwR8wYABuv/12lYUfOnSoCs5vuukmxMZqjb0mT56sMvIS4Mv3JGCXwJ6o6kviS87mto6JQMu6YdibmIYFO07ixu4NYVT/W7If0xftVdcfG9YSky5rAU8hyw++vru3mhO/7Xgyxny0Gj/c1xeh1Tx6zpWkU/avm4/DVsUnafyzTBg0JBeRHlyR9PW6o5i734Re6TmIifLM48jPt+HdpQfx1z4T+mTkoE6kZx5HRexLTMXPm447/u2hknnuv2iVwJJ4Iqo2kqUqQ1l6sWRNuSVPe3xFAvZykBJ3+RAnQbmsUf/rr78wffp09b3HHnsMCxcuxOuvv47mzZurteY33HCDKk+vDp9++in+8Y9/qBL9OXPm4JlnnlH707t3b3Ui4ZZbblH7/fvvv+P555/HN998g2uvvbZa9o18N8Ne0hp2nZTFv7Fwr+pobtSAXcpR3168T12XrPrE/k3hadrVi8Scu3vjlo/XYPfJVBXw/mOw55x0cHbwdBpenbermubMm/D5qng8OKQVPNH+U6l44f92Id9mwq2frMPsu3qjTkQQPIn8np/+cRvmrJeScDmO9fjqrt6oHR4IXzBt4V7In/rwdnXRsUGlFgB6Pd8O2A1epkZEVF2CgoJw3XXXqcz6/v370apVK3Tt2lV97++//1ZZbj0Iloz74cOHXfJzpcRd1qrLWnY9yy4/TzL7sg86Wacu21NPPaXWu0spvQTsomXLlmp75JFHMGbMGBXgM2CnqqJX55VWEi9GdtIC9pUHzuJMWjaiw4z3IfzNRRfKUT0xWNe1qBuOZ69qi398vQkfLT+IcX0aX/T3Y0TTF+1TQVy/5rVUH4Gqsjn+PN5ecgAfrTiMcf2aIjLY4rHBnth/Oh2jP1yNryb2Qr2oYHiC3Lx8PPrdFvyy+QSkr2Owvw17T6Vh9Aer8NVdvRAb6RnHUVHbjyfj9+0nVU7j0WGeedKoOpl9e6wbS+KJiJzL4mWt+I4dO3Drrbc6bpe14T/++KPKwsu61meffbZIR/nK/EzJisu6dMmWnz59WjXAu+2221RX+kOHDqku9qNGjVKd7KVMft++fRg3bpwqy3/88cdVtr9JkyY4duyYalx3/fXXu2TfiIqjV+eVVhIv4qJD0alBJLYcS8a8bQkY1ycORrI3MRU/bfaectSrOsTivT/3qyz7B8sP4onLW8OT7DyRgv/boi2fenpEG1U5UFX6NqmBb1ftx8nMXHz810GPC5gk2Ju3TQv27myZh/mJoTh0Jh03zliFr+/qjUa1yr4czB2kI7qcXJq/4yTMJj9Mu7EDTu3ZiJmHwnDwTDpu+mAVZk/sjYY1jX0clfH6gj3q8upO9dCybri7d8fwfHysGzPsREQ6afhWs2ZNFRRLmblu2rRpqjFd3759VdAu68n17HtlhYSE4I8//lBd6aUUX4Jv6VQvjef07+/evVsF4ZJFl5FvDzzwgGqCJ13hz549q4J3+Z6sbZfmeVOmTHHJvhFVJsOuZ9mFdIs3mmkL9qrxbZe3i/GKclSTyQ+P2QPPT/8+hFOpWfAk0xZqAcyVHWOrNFgXMqpvREPtpKssIZAKEE8M9q7qEIMONW2YPbEnmkSH4nhSJm78YCX2n7owFtRosqx5uOeL9SpYD/A3Ycat3XBF+xjUDgZmT+yBxrVCcPRcpgraZYmEN1p/+JxqyCl/hw8P8fyThdXBRzPs2mVKllWVHhltxigRkTtIGfqJE0UDi7i4OCxZsqTAbRI0OytPibyslXfWoUOHIs+vkyz7Tz/9VOz3AgIC1Kg3IiNm2IWMdHtl3i6sP3JeBRP1DVKuu+1YsgoYJEM52Quy67rBbeqoRnSbjybhvT8P4IVR7eAJNsafx6Jdp1Rp9CPVFMB0rGlDh/oR2HY8Be8vPaCWFHhasPfQZc2xY80xxEYGYc49vXHrx2tUo8ebP1ylJghI80cjycjJxcTP1qtlMkEWEz4a1x39W9SG1ar9myL/Pnx7Tx+M/XiNOulw0wdamX+rGO/JQMv7/3//0E643NS9oapEoosz+XLALp8ZU7OYZSciIqKylbKmZeeWqemciIkMQs84bVTRb/ZyZyNlKK/pXN+rylFlyc7jw7Us++w18eokiSd4w/77uK5rAzSvE1YtP1NO1jwyuLm6/sXqI0hIzvSwYK+Bykbr6oQH4Zu7+6BtbATOpOXg5g9XY+uxJBiFxBvjPlmrgvXQAH98NqGnCtYLqxshx9EbbdRxZKuTD7IEwFus2H8Gaw6dQ4DZhH/Y//7o4nwyYDebgNBAf3WdZfFERK4jTevCwsIKbBEREWjQoIHKpBN5sqTMHEewE1HGRl36THbpFm8E6w6fw7K9p9Xa2YeHeGY39dL0ax6NPk1rIScvH28v0jrgG9nKA2fw9/6zsPj74aFq7m5/SfNa6oSSnIiS8X4eE+z5m/BgMeMHa4YGqDXsUmUhlTBjP1qDDUfOwQijICVrLpU2EUFmlf3v1bRWifeXBpVf39VL9cCQOEXGFUoVhqeTEy6v20+43Nqrsdc31nMlnwzYRZT9jVafp0pERJUnzeE2b95cYNu4cSOWL1+O3377zd27R+SScnjpql3W5XQj2seq4HjHiRQccPOaVOcMpYyaa1zLO8tRH7Nn2b/feEw1I/OEAGZMz0bV3mRMKhL01+rbdUfVmD9PeK3G9m5UYjf4yBCLCoh7NqmJ1Oxc3Kay2mfgLlqWXLL9yeqEgoyf69KoxkUfJz0y5Dh6xNVAalYubvt4DVYfPAtPtnBnomrCGRLgj/svbebu3fEoPhuw6yMsONqNiMh1wsPD1az2wlvTpk3RuHFjd+8eUaWcT88pczm8rkZoAPq3iFbX9S7g7vLXvjNY6wPlqN0a18BlreuoPkXTF+6FUf255xQ2xiep9cyTLnXP70MC24Eta6vxfjLmz+jBXrDFH/cPKv21Cgs020vOo5GRk4cJn67D0j2nUN0SU7LUmDaZXCCz1aXUvX39sjcUDA+y4LM7eqoxf+k5ebj907VYvvc0PJH8v/jGAu3va0K/OEOOuTQyk69n2JNZEk9ELla4qRp5Lv4uyZm+jC6qDA3niu0Wv+WE2/6mVIZyge+Uoz5qb6Ynr/muhBQYTX6+VDtoAcz4PnGoExHktn3Ru+vLmD8Z92f0YE+C34sJDvBXTd2GtKmD7Nx83PX5eszffhLV5dj5DNXp/cDpdNSLDFLN5CrSLyIkwIxPxvdQJ6CyrPmqad2inYnwNL9tPYE9iakIDzLj7v7MrpeX7wbs9jdbfTwLEVFlWSzavysZGcYtK6Ty0X+X+u+WfJu+jK48GXYxrF0MAs0mHDydrkrj3eGPHYmqLNdXylFlNJqMSBN6sGck87YnqBMJkg2+d6B7fx8dGkSq8X5yLknG/Rk52LtnQNlfqyCLP96/tRuu7BALa54ND8zeWC29JA7LLPUZq3DkbAYa1QzBnHv6qLFzFSXHoY9/k94M9365Qb0mnsKal++odJG/dVm2QOXjk2PdCpTEM8NORC4ic8GjoqJw6tQpxwxxWSNYUfn5+cjJyUFWVpYaueapPPE4JBspwbr8LuV3Kr9bOQ7ybRXNsEtQJiPH5m07qcriy1MW66oMpT7n+45+TXymHFVGpP2+LQGLdiViU/z5Mq0drg65efmYZg9g7rykiVo24W4y3u+PnSfVuD8Z+ydBvNGCvXsGNC13sGfxN+Gtmzsj0GLCjxuP46FvNqlZ6DJSrCrsS0xVDeZOpWajae1QzJ7YW02LqCxZxvK/MV3w2Hdb8PPmE/jH15uQbc3H9d0awOh+2HAMh89mIDosALf3jXP37ngk3wvYbTb45edeKInnGnYicqGYmBh1qQftlQ0aMzMzERwcXKnA3908+TgkWNd/p0QVzbCLUZ3qqYD9t60JeOLy1jCVsWmdK8hJAplPLR2q7xrQFL5CRqTJqLTvNxxTWXZp4mUEP206rqot5MTPxP5NYARSri1j/mTfZOmErJ02UrBXKzQAE/pV7LUy+5vw+g2dVKZaxv398/utKmgf18e1weOOE8mqyd259By0jgnHF3f2KlP5fnmO442bOqvj+GbdUTz63RZk5eZhbC/j9ofJzs3D24u1aQ33DWqO0EDfCz1dwbdetfNHYH6rE0b4WZAwcKW6iV3iiciVJCCNjY1FnTp1YLVW7oSgPF66qw8YMMCjS7I99ThkXyWzTqSTD+KiRgVKOge1qqMy7TIbXEY0dbfPZ6+WDKW9mdg9Uo5axnF03kJGpf2y+bgaCbbqwFn0aVbyOK3qICPU3tIDmIHNVGMxo5Axf3JyR8b+yfi/HtX0N1q2YK9ZpYI9OUH2yjXtEWT2x8y/D+G5X3aoDLWrTmBtPpqEcZ+sQUpWLjo2iFRN76qickKmU7x6bQcVtM9aeRj/+mm7WtsulRpGJCdITiRnITYyCGN7NXL37ngs3wrYzUHwgw1mWw4ig7RD5xx2IqoKEuhVNtiTx+fm5iIoKMijAl1vPQ6iCyXx5f8gLh+wh7Wrq8pyZR1tdQXskl0+4sPlqDIq7eYejfDF6iMqc/z9vX3cWukzZ108jp3PVJlXV2d4K0vG/N3Uo6EKsmT835y7e7v1tdKDvZiIINzau/JZZDmWZ69qg+AAE9798wBembcLmdY8PHhZ80odp0xeuGPWOqRl56oJBZ9O6IGIKjwRIycfnh/ZVlvbvuwAXvptp6oYeMBNkwZKkpGTi3f/3K+uP3hZC7W/VDGesZjQVSwXOqLWDNS6tHKsGxEREVV1SbxeFi/mbUtQ65irmnyIZzkqMOmy5qrp34Yj57F0j/vGYmXm5OF/S/QAprnqZG40sl+yXlqCUBkDaIhgb3BzlwV7Epg/Prw1HrNPEZBeAv/5Y0+Fpzes2HcG42euVcF6n6a18PkdPas0WHc+jicub6X6NAg5wSJz6o002eTTvw/jTFoOGtcKwY3djb/W3sjMvhqwRwXkqctklsQTERFRGeiTZSpSEi/6NY9GzdAA9SF25YGzGNCyNqrSV2vikcByVNSNCML4vnH4cPlBFdjI3PHq7CGg+3zVYdWMrH5UsMr6G5GM+7utd2N8suKQqkiQWebuyLLrwZ50Wa+KBnGT7Bnfl+fuwvtLD6iTKZK1Ls+xLtmdiHu/3KiWOQxqVVt1ci9wYkEale5fBKQcB3KzgbxsIDcHyM2yX9c2f2sWOh9PgGnZFqBGIyCivn2rBwRFSnRe7M+XfX1oSAtVMfDqvN1458/96iTdv65s49rfmZwEyM8D8q1Ann3Ld7r0MwHmYMDPAj9brqNH2AfLDqjrclJBmv9RxflWwO5vgc3PH362PNSwaAE7S+KJiIioLJIqURIv5EOrjGaSQFrWCldlwJ6enYv37BnKfwxmOaqMk5IS650JKfh9+0nHyLfqkpplxfv2AEaCLMliG0JOOpB+Gkg/o13mpGNSm8b4fm2+GgMo4wAvb1+9jTcLBHtDW1RZsDexf1P1/8UzP29X68FlzfzL13RQ68QvRqpkpFN7br4Nw9vVxdtjuiDQ7PT/2NF1wO+PAyc2XfS55OhUwf+K5UW/GRCmBe6yhdcDLEGAyaJiGrWZLLjb34JuHdIwb+cZWFeZ8fvxMFzRIhR+OWlAduqFzflra4YWiMOmXTpfd77MzwXyypbclNOYo+RRW/wRZArCYps/8oKDUPfvGsC60AvHobYGBb82OzXnk5+dlQyknABSTwApCUBqgva1bCYzEHcJ0OxSoHbrEk9oeBPfCtiF/KHnpCPSor3ppmRZ1biTsvzPSURERL5JSk31ZXQ1Qite8ipl8RKwy/isl69tX/BDvgtJAHI2XStHvcEDRj9VNalskMZc0vBNRtxJEFqdn/0kYy0nfGTU13Vd6lfbz0VmkhY0Ht8AnD+kAnP/tFMYciYe5u33aoFbITL8bqO/P/YG1MexX5ohP2UoTLEdgZj2QHAFRuPl5QIZZ+0nBgptmedVjylYQoCAULWt3peKgTnJiKxRA6MiwoDjiUBQFBBaGwgML1+AlpUCJMUDSUe0S8l2RzYEYjsBMR3U2ngJ2v/5/RZ8vfaoauD23xs6qo7sJflp0zE8+u0W5Nu0/5/fuKnThZMKqYnAoheALbO1rwPCgSb9tWOUoNQ/QLtU17XLPD8z9u7ahpYx4fBPO6nto2zy2kiQfWavtpWim2z6P0sJ9q0qSVZdP3FgywesmVqAL9+y5SEwLx215ddkSwHO2KfmnNhY8vPJ7zY8BsjJ0ILzYv4uC9gzV7sMqws0HQQ0vRRoOlAL/r2Q7wXsUrKRk44w/1zHSRw561nRs+VERETk/aT7s5zgr8wadiGdt6VEXUrVZT318Hauz16yHLV4MkLts1WHceB0uhpfVl0nMs6n5+Djvw6p65OHtiw5GJTg8sw+4Mwe4PQeLUiTzHdkA6BGHFCjsf0yTstQ+hf6GC/l1onbteBc34oJ9OSnhzrfIMFkaB0gNFq7fmon/LOS0MYUjzbWeGDBnxfuG9UIiOkIBEbYy6JztIC8uOsSfKmg/Fy5Xq/hssn/YhIDflHomxLkSnAXGg3/kGh0OZ8F05J1QHhdLRhOPqqmQjmCdAl6S+QHRLfEDfU6o033OLy8MRALN2XgQWse3rq5S7FVEN+sjcdTP21T8cNN3Rtg6nUdtRM/8tqv/QBY+m8gJ1W7c+exwODntX0rRb7Vir3n56H5iBHwd27Mqgevycfs2eYE+2tsf31V9lsvT9cy4cfPpWBtfBpSbcGIqV0bgzs3hb+U1cuJjsAw7VJOIsgyYQm61ckPP6frcLrN70JQLlltldUP0G4zFXptbDZYs9KxcN6v2G2LxS8bDqJj3UC8fX0r+MkSgOwU+7Ect2fK7Scl5Lp8Xz+B40xO0qilAbFAeOyF65J9P7gUOLISSEsEts7RNhHdSsu8SxDfuB8QFAFv4IMBe5C6CMjPUeNVpEmEnPFkwE5EREQXazgXZDFVqrxc1k5f1TEWH/11SHWLr4qA/aPlB9UJhpZ1wzDS3ujOMGQtrGTPJHCo7PMcXQOcP1wgW1nkuiq19VMZ3vC0RLzdfDs27twN87xZyN9jhin9FJAmWyJg8tfWDOubBKVOX5sCwtD01H6Ylm8HrOlAdrJWXiyBtgQk6lJKj9O14Eiy0cE1cCrFjOfyAP/ImhhxfhewTm6P0oLJ03vtAfperfy3OMfWFr3Nz98eyDfWApmzB4CELdra6MKiGgMNugN12qjAPDeoBlZu2Yc+Q0bBEhmrZbads9YSjSYfw9yFC7Bn80r0CDqKS8IT4KeCYPtWXhIQhtRyBNvaZW3tNdKD+5w07DicgIQzZ1EnMBcd6ljgJ6+lZJmlUkBeczm+FAlgj6kTD6oTwKoVpf/s4JraiQZ5rSTwk7+ZE5sByWbLa39mD9oB+FpiZQtwcG8MNk1vg669BsLSoCsg1QXBNfDp34cw5f92qqcc16cxXhjZTuuFIOvUf38SOKs1eES9rsCI/2qveWUEhAC1mmlbGUntRvD2k/jn1xthTbBhaI26eOeWQuX6VUH+fsyBOJ0Xihlbs5Bji8GLI3rCr+FFlv3I31rGOS14l4BeKi1U+b/8XYaU/Lh+DwHWLO3/jQN/agG8VJLYf59YMwNodx1w46fwBr4XsOuN53Iz1SxSCdiliUxcwXONRERERA56z5vKZNd1EkRLwL54V6Jaa+7K7u1n0rLVnGkxeWgr9y/5y8mAX/xatDz5K/y//gw4vk4Lbut3A1pfCbQeCdTWOl1flHy4l6zx9h+AHT9pH/DLaYBs8nJLK6PCyWe5TU4mlPC8EvJ0kCvHy/CDJMtqf55WssnPlFj6z59Kf5yU+Ea3BGq30rKFYbW1DKtkjSXQlKyxXJfAVZV5HymalZTXVjYJGOVSAmQnNqsV5/fbtEx9caM2JfiKaohLrx6P5/c0wttpOZg6vAPGdAgHTm4HEndoWVH7Gmrn9dQFrstn7rA6FwJzOSFSioTkTFz736WqidusW3vAr1WdYtbbn3Gst89NOYm9G1egVcNa8Jcsfm6mVu4uwbmcpFCXjUrOsqaeBBK2AgmbtZMdsiUfRVPTSTRNPwksuVBZkBzUAHXS6+F+/yZo2L4vbh7SC35Jh4E//nWhPFuOc8gLQKdbimagq5Es9/hwXHfc+8UGLNyZiLs+34APbu1WLVMJFhw3qd9fz7iaGNCi4N9dseRvLVRO5NTSToyUd5lzkwHahue1wP/wCuCgPYCXLLuX8NkMu/xDExUSiuNJmRztRkRERGXqEO+KirwO9SMRVysEh89mYNGuRFzd2XVrmt/78wAycvLQsUGkaoZVaRIk7fld+wAsn6FCamqZUrU5X6+lBWgSTMWvBuJXaZcJm2HOz0Wbws+rl2wvflELUFtfBbS5SstOFs72SoAoQbpszgGqZL7rddGy7arjtnTfli7c2QW7ckvpsARTEjyG1cWe9GD8fjgfWQHReOTa/gisUU8LavVmV7KpjHmy05aC/MzzOHH0MGKbtIZ/sD0Dr7Lw9kupGpDrllAtYM9Mwpy/tmLrvsNoE5WHsR3D4SeZ4qwkLbiQ+8vJCgnMVYDeomxrxKX7uGSH9SBeMs4SqNbvrmVjXdSEKyTAjPsHNceLv+1U4wGv7TIIQbIeW7YqICPvJNjrEVdDdfIvwr7GXWXK7Sce9p2ogRZDC5WSl5WsmZat5bALt6Wfwc6Nf+GPhfPR0nYQ3QLiEZN3EpFZx3Clv2xrgT1zgP8+pFUNyPptKRfvdS8w8J/a36QBXNqqDj69vQcmfr4ey/eexoRZa/Hx+B6quriqxJ/LwKpT2t/eY8NbVf90gZCaQNtR2qb/f+IlfC5gt1mCpTBKlVFEhUSp25LZKZ6IiIjKNIO98jOW5YOsNKt6e8l+/Lr5hMsCdslQfrlGC2gfHVaJD8wS7EqZrwTIEqxfrAGUc58gyXIWYguLwXFzY8T2vAb+TfppwfPeP4DdvwEHl2nrrFdM0zYp75bMe7PBWsZT9kFKXHUSDLceAbS/Hmh2WcHu0mUUl5uH715fppI2Nc+3xt0dylZynGe1YsO8eRhxRdkCxKPnMvDMvkxY85pj9rW94Ne8DBnHspDsrd5du3EfVKVbejXCR38dVD0XpFmiNO6rCvFnM/DtuqPq+mOV+dutrNBotO1/LTIbDcLtn65FanouIpCGdqYjeLB1OvqGHNX+Ls/u14J1aXZ2xb+1Ey4G07d5tJoLP+HTdVh98ByueGs5YiLsicsqIH8j+TY/DGhRCz2b1ITbmbynd4cPZ9gzHWfJ9bPmRERERMU5n+66kni9LF4C9uX7TquTAa7I3L+9WMtQyoflMpWjOpOmVYeWaQHyrt+09dk6KZ1uM0r7DCXdvh3buQvXpfGVHqzXaQs07AU06gM06o3c0Fhs+P13jOjhFOh2n6Btkr3etxDY9X8XZlav/VDbdLImvcVQLUhvOVzLslaCrOeV0Wr//H6rmsE9pmcjhAdV/kRMYZKVtubZ0LdZLRU8eSLp1yBjAZ/6cZsaE3hzj4YuXcKhe3PRXjUiTUYd9mpaC+7WrXENfH1Xb9z2yRqczwjD8CtvRN9+TicrpF+BlOFL6b2Bx4p1j6uJr+7qhds+WYuj5zLVVpX8YMMjg1tU6c/wRb4bsFszERVsKTBXlYiIiKi0DHuUCzLsokXdcLSOCcfuk6mYv/0kbu6p2meVTpqUSXMx6SQuHZUlIJY1yiYTjpxNx3frtQzl42UtR5UO09Jpeecv2pZx5sL3ZOZz++u0rXCZemFSSi7N1iRwl2Zqhcu6raV8zpIS4g43aJs0kZKTBhK8H/4LqNlMu10y7i4uNZbRajOWHcDB0+mYueKwCuBd6cDpNPyw8ZijPNiTSTd9ea2OnM1Q4wIfuLS5S59/X2IqftqsNQZ4bFgZ+xlUg/b1I7Fo8kCcSctBq5hCTRJl6YOHdCDv2CAKix8diPWHpWO+NumiKuTm5uHIzg1oX98zXhdP4rNN52TEgP6mK+NPiIiIiKqj6ZxuVOd62D1/j+oW7wjYZT22BOVn98N0ajc6xy+F/+fvaSW4zgG1c5OyViMw/1RrmPJjMLBlPTU6rkSyhloy2VLqLplt50x6SDTQ7hotk92wd9lLSiWYd0UAI02kJIMuWxWT0Woy8u7Brzfh478OYnzfxi6dGDR94V41p3tImzro2qgCs8sNRMYCyji6h77ZrMYFyuxyadzsKtMW7lXnfKTnggSXRlIrLFBtni46LFA1o6tKVqsV8wr1QCTX8O2S+OCAAmfNiYiIiEpvOue6QGVkx3r4z/w9WHXwLE4lZ6LO0XnAohccY7Okp7Nqr3XW6UEy7qhWc63J2aHl2jiyDZ/iHllvHBiMPP/BwLZrtRJyPSt97pAWoO/9XcuoSxM25yC91eXaCKQmA4vO9vZiV3aIxXtLD2BXQgpmLDuIJ69o7ZLn3XkiBb9tTXB06vcG8rcqDQ33JKaqsYGuqhrYfjwZv28/qc75SN8FIirKd/5Vdmo6B73pXKSlwFlzIiIiouIkVUGGvWHNEHRtFIX8o+thm/kakLxV+4bMIq7VHPk1m2HvORua97oc5rqt7YF6eMHmcIf/wrJfZ6F18l+o65cEHPpN22SsVuO+2ozx07sK/uDarYFWV6jMvFZSX/XjnoxIZmg/OrSl6qQ9a+Uh3HFJHOqEV74p17SFWpO8qzrGom29CK95rSYPa4l7vtigxgbe3i9OZW0r6/UF2mt1dad6aFm3UNk5EflmwF5wrJs9w86SeCIiIipDhr1GqOVC2XriNuCkbNu1BlTSMbrt1UBMh7I1oko+hv+a/odmgb8DyfZA/ZJHgD6TgIAQ1ZV8z7x5aNZ+RPHzss2B2BrUHeNPZcPkNxpLx0Si0ak/gT3zgNO7tfXgws9fC94lQJdses2mrn55PNbgNnXQuWEUNh9NwrtL9mPK1e0r9Xwb489j0a5TMPkBjww1znpsVxjWti46NYjElmPJKtv+3Mi2lXq+dYfPYeme0/A3+eHhId71WhG5kg8G7PYMu/MadpbEExERUXFyMtQs8cEpv+BW8370WvRv4Pu9xY4vU3PF/3odqNFEC9xlkznhhYP37DTg7zeBlf9Ds1xtFNIPef3R9463UL9R+YLp1xfsVZdXd26IRh07AxgADHkeOLMfOLBEm03cfHDZ5nv7IGnOJ036xn68BrPXxuOuAU3RoEZIhZ/v9T+0jPH1XRugWe0weNtrJWXr42auVeMD7xrQBLGR9s/V5WSz2fBf+2t1U/cGiIuuXOd/Im/mewG7NDSRf3SsGY5ZqiyJJyIioiKOrgW+uUV1ZJ+sf2rS+75JNrxuO6BueyCmvTYffM9crZHb+UNaQC5bZCOg7Sig7TVa8L71G2DxS0DaSe15GvfDs5lj8FV8TTx+IB8PlKFZvG7toXNYvvc0zCpDWajLeXRzbaOL6tc8Wo1eW3ngrBrF9p8bOlXoeVbuP6Oew+Lvp0aheaP+LaLV2ED525MxglOv61Ch5/lr3xn1HAFmEx68zDtfKyJX8emS+Eh707mULCvy8m2qJIeIiIhIjRb7YaL6vGALrY0lKQ2wy9YIE64bhdBGnYGaTYqu/e48Rsue71+ojUnb+weQHA+sekfbJKi3pl+YbT70JaDNSHRcfxRfxW/D/205UeaRWZKh1LO5N/VoiMa1mKGsDGmidt17K/HDxuO4d2AzNC1ndlxljO3rsW/p2Uj1J/DmioQbZ6xSYwTvHdi03H976m/X/lrd2qsx6kVVLEtP5CvKOK/DC0virRKwaxl2GSWRmsUsOxEREQFYPQOYc5sK1tHycpy6Yw3utD6OaXmjEdz5ei1zXVKjtsAwoN21wI2zgMcPADd9AbS/AQgI04J16e4ugfoDa7XMu58fLm8Xq7KyMpN9b2JqmXZxuWQoD+sZSmbSK0tGrw1uXUclcKYv2lfuxy/ZfQqb4pMQZDHhAS//fcjYwEGtaiM334Y3K/BaLdiZiK3HkhES4I/7L21WJftI5E18LmC32Uvi5U1Y3uRCA/wLdH8lIiIiH5WfD8x/Gpj/hHxiALrfAYz+Cuet2gl+aVYr3bLLLCBEC8pv+EQL3u9cCDy0Bej3D9UwThcZYsHAlrXVdcmylyVD+YY9Q3lb78YVXkdMBUkXdP13IKPZyio/3+boJTC+r2s6zRvdY/YRbD9vPl7mk0xCTohMs79WE1zUaZ7I25UrYJ86dSp69OiB8PBw1KlTB9dccw327NHeMEoya9YsVT7jvAUFufEfMn2sm71ZjN4pXu/+SkRERD7ImgV8fzuw+l3t68HPA1dOU3PJz6dbKz+DXRIGDXtqTeCKMbJTPXX565YTKiAvzR87LmQo7xvEDKWrtKsXiSs7xhYYzVYWc7clqFnu4YFm3DvAN34f7etH4or2MapKVT95VBZyMkRmuYcHmXF3f994rYiqNWBftmwZHnjgAaxevRoLFy6E1WrFsGHDkJ5uX49VgoiICCQkJDi2I0eOwO1r2OWN2enNl6PdiIiIfFTGOeDzq7V15zK//LqPgf6THd3dk/SRbi6cwV7Y0LZ1EWzxx5GzGSoYLy1DqQdId/Rrwgyliz0ypKUaySaj2WRE28Xk5uVj+kItY3xn/yaoEVp1fyNGM3loS/W/iHYCKemi97fKa7VIe63uGdBUVZYQkYsD9vnz5+P2229Hu3bt0KlTJ5U9j4+Px4YNG0p9nGTVY2JiHFvdunXh7jXsfrkFA/ZklsQTERH5nnOHgE+GAkdXA4GRwG0/AR1vLHAXfZqMPl2mKoQEmDGkbV1Hlr0kv245jn2n0hARZFYjyMi1mtcJUyPZRFkyxz9uOo6DZ9LV38adlzSBL2lRNxzXdq6vrutLAkrz/YZj6oRUrdAATOjnW68Vkdu6xCcna2eAa9YsvrxLl5aWhsaNGyM/Px9du3bFq6++qoL+kmRnZ6tNl5KirSOSjL5slZHnZ1YHbcvJQK7Visgg7SU4k5pZ6eeuTvq+etI+F4fHYSw8DmPhcRj3ODz9WMhpbvrs0WpsGyIaALd+D9RpU+Ru+rI5fRldVRnZMVaVDP+29QSeHtGm+AzlQq3J1z0Dmzma55JryUg2WZv99/6zWHngDPo2iy72ftm5eXjL3nRNliaEB/ne7+PhIS3VCSYZLyhj2mTkW3GyrHlqZJ64/9LmCA30vUFVRBVV4f9bJPh++OGH0a9fP7Rv377E+7Vq1QozZ85Ex44dVYD/+uuvo2/fvtixYwcaNNDOYBa3Vn7KlClFbl+wYAFCQio3JiMy4xAGyT+y6UlYMG8ekk9LkYEJ67fuQu3zO+BpZGmCN+BxGAuPw1h4HMY7joyMDHfvBlXWqV3ArKsAawYQ0wG45TsgQlu/XNiFkviqDcgGtqqtMueJKdlYd/gcujWMKPD979YfQ/y5DESHSYYyrkr3xZfJSLYxPRvh81VH1Oi8H+6rpapFC5uz7iiOJ2WiTnggxvXxzd9Ho1ohGN2jIb5aE69eqzn39C72tZq9Jh4JyVmIjQzC2F6N3LKvRD4XsMta9u3bt2PFihWl3q9Pnz5q00mw3qZNG3zwwQd46aWXin3MU089hcmTJxfIsDds2FCtl5f18JWRm7Ad2AME+QMjRozA7oX78HfiIdRuEIcRI1rDU0h2Rz40Dh06FBaL557R5XEYC4/DWHgcxj2OzEytcSl5sD2/a8F6va7A+F+BwPAS76qXxFd1hj3Q7I/L28fg2/XHVNbSOWCXDOX/ltgzlIOaqxJ6qjqTLm2Ob9cfxcb4JPy55xQua11wOWdmjvw+9qvrMlYvyFLCmD8f8OBlLVS5u4wZlHGD+sQDXUZOLt5bqr9WLXz6tSKqiAr9az9p0iT89ttvWL58eYlZ8pLIh7UuXbpg/37tf9ziBAYGqq24x1b6w16w/Q05N0s9V80wrQldalauR36QdMlrYgA8DmPhcRgLj8NY5Bhyc3PdvRtUWfZeNqjfrdRgvbqazulGdaqvAvbftyXgmSu0MWPiK6cM5S3MUFa5OhFBGN8nDh8sP4jX/9iLQS3rFPj+56sO43RqNhrUCMboHr79+4iJDFLjBT9ecUhl2Qe0iC6QZf/078M4k5aDxrVCcGP38sUNRFTOpnMyZkSC9Z9++glLlixBkyblbxiRl5eHbdu2ITa2+LKz6ms6lykH5Gg6p589JyIiIh9gtVdJOM1DL0l1NJ3T9W5aU5W8y8/8+8BZdVt6di7e+3O/Y301M5TV496BzRAWaMbOhBTM257guD01y4r3lx1Q1x8a3AIB5nJ9nPZKsoY/NMAf244n448dJx23J2da8YH9tXp4SAtY/PlaEZWXqbxl8F9++SVmz56tZrGfPHlSbc6lgePGjVMl7boXX3xRrT0/ePAgNm7ciFtvvVWNdZs4cSLcOodd5GY7yts41o2IiMiH5GYXHPdaiupqOqd2x9+EKztoSY25W7XA57NV8TibrmUob+jGDGV1kRFteuf3aQv3qhFu4tOVR5CUYUXT2qG4tovWJd3X1QoLxB321+qNBXvV+EHx0fKDSMnKRYs6Yap6hIiqOGB///33VeO4QYMGqQy5vs2ZM8dxHxnzJrPWdefPn8ddd92l1q3LmnFZj75y5Uq0bdsWbuH8xpyb6TTWTXszJiIiIh8qibdcPGCX4EzUCK2eJR2jOtdTlwt3nUJyDvDx34cdc6+ZoaxeE/s3UZ8VD55Oxy9bEpBuBWauPOL4fcgJFtJM7N9UNU2UsYMyfvBMWjZm/n1Ife/RYa3gLwPuiahq17BLSfzFLF26tMDX06dPV5th+FuQDxNMyFflcDVCtHVrLIknIiLywYD9Ihn2/Hxbta5hF10b1UD9qGDVgfzD3f6qz06ruuEY2VEL5Kn6yKi2+wY2w9Tfd+OdPw+gWZAJ6dl5aBsbgRHt3bS806BkzOC9g5rhP/P3qPGDm+OTkJGTh44NIjG8XcGmfURUdj55WjDfZH/DtWYiMli7npJldZTvEBERkZcrY8AuwbL+8UCvyqtq0rDrqk5aMHgsXctKTh7WEiZmKN1CRrbVDg/EsaQsLDupfXR+bDh/H8W5vW8cosMC1fjBz1YdcWTXixv1RkRl45MBe57J/oabm6XOBgopHpAmIkREROQDrGUL2PX16yEB/mrsWnUZ1elCNr1j/QgMa8sMpbsEB/ir0W26Lg0jcWmrgl3jSSPjBh+4tJnj655xNVXXeCKqOB8N2O0dYa1ZqrOndLV0XqNGREREXi63fAF7dZXD66Tkum2stmxv8tAWzFC62c09GqFRzWD4wYZH+fsolYwdbCivlR/w+OXMrhNVlm8G7H56hj2zQNdX/U2ZiIjI17377ruIi4tDUFAQevXqhbVr15Z6/zfffBOtWrVCcHAwGjZsiEceeQRZWfag2Mhd4i/SdE4/mV9d5fA6CXI+vq0rHuuQi37NalXrz6aiJMEz+84eeLRDHno1qenu3TE0qUT57p6++L9Jl6BHHF8rosryzYDdsYY9q8CbMEe7ERERQU1/mTx5Mp5//nk1krVTp04YPnw4Tp06Vez9Zdzrk08+qe6/a9cufPLJJ+o5nn76aRiW/aS9UTPsQtZNNwyr9h9LJagbEcTfRxnFRAahff1Id+8GkVfw7YDdkWHXR7sxYCciIpo2bZoayTphwgQ1hnXGjBkICQnBzJkzi72/jGvt168fbrnlFpWVHzZsGMaMGXPRrLwx5rDbl8mV4LybMuxERETlHuvmLfL1knhrwZJ4fWwLERGRr8rJycGGDRvw1FNPOW4zmUwYMmQIVq1aVexj+vbtiy+//FIF6D179sTBgwcxb9483HbbbSX+nOzsbLXpUlJS1KXValVbZeiPL+15zNZMyMraXD8LbKXc72yqVo0XGWSu9H5VxXF4Ah6HsfA4jIXHYezjsBrgeMy+XRJvD9jtneI5i52IiHzdmTNnkJeXh7p1C3Yll693795d7GMksy6Pu+SSS2Cz2ZCbm4t777231JL4qVOnYsqUKUVuX7Bggcrmu8LChQtL/N7wtGRIMfxfq9YhJaT4Un+x5aAUI5pw5vgRzJt3CO5Q2nF4Eh6HsfA4jIXHYczjyMjIcPeu+HjAbu8Q6yiJ5xp2IiKiclu6dCleffVVvPfee6pB3f79+/HQQw/hpZdewrPPPlvsYySDL+vknTPs0qxOyukjIiIqtT+SEZEPW0OHDoXFUnwpu3nXP4Bc4JJLhwC1WpT4XH/M2QIkJqJ7pzYY0acxqlNZjsMT8DiMhcdhLDwOYx9Hir36y518O2B3ZNhZEk9ERCSio6Ph7++PxMTEArfL1zExMcU+RoJyKX+fOHGi+rpDhw5IT0/H3XffjX/961+qpL6wwMBAtRUmH5Bc9WGv1Oeyn7S3BIXJHUt8juSsXHUZHR7ktg+hrnxN3InHYSw8DmPhcRjzOCwGOBYfbzpXMMPOkngiIvJ1AQEB6NatGxYvXuy4LT8/X33dp0+fYh8jJYOFg3IJ+oWUyBuO7FNZ57Cn603nqr9LPBERkW9m2EtqOseSeCIiIlWqPn78eHTv3l01kZMZ65Ixl67xYty4cahfv75ahy5GjhypOst36dLFURIvWXe5XQ/cDdkhvgwBe5Ibx7oRERH5ZMCeX7gk3jHWjSXxREREo0ePxunTp/Hcc8/h5MmT6Ny5M+bPn+9oRBcfH18go/7MM8/Az89PXR4/fhy1a9dWwforr7wCQ9Kz62Waw66dzK/BsW5EROQGPhmwF57Drr8JM8NORESkmTRpktpKajLnzGw24/nnn1ebR3Bk2P0A/5ID8SxrHjKteeo6S+KJiMgdfHMNu5+eYbfPVrU3nZMu8Xn5BlxrR0RERK5jP2EPSzDgJ9PYi5dkz677m/wQEeSTOQ4iInIz3wzYTZYCb9iR9jns0oMmNYtZdiIiIq+mZ9jNRbvUOztvXyoXFWxRJf9ERETVzUcD9sACGfYAswmhAf4FzqYTERGRl3J0iA8uW8DO9etEROQmPp5hv9B0Rl+bpr85ExERkZeyn7C/aIbdPtKNHeKJiMhdfDJgL9wl3vnsORvPERERebmyzmB3ZNgZsBMRkXv4eNO5ogF7MkviiYiIfCNgt5R1BjtL4omIyD18M2Av1HTO+ey5/uZMREREvp5ht5fEhzLDTkRE7uGjAXvBsW56B1jnN2ciIiLy9i7xZS2JZ4adiIjcw7cD9txiSuK5hp2IiMi76UviLhKw65Nj2HSOiIjcxcfXsDtn2FkST0RE5BPKOYeda9iJiMhdfLtLvGTYbbYCGXaWxBMREXk5vcLOElymDDu7xBMRkbv4dkm801l2R9M5lsQTERF5t3Jn2BmwExGRe/howO5U2mbNKDTWjSXxREREvt4lPi/f5uhrw5J4IiJyF58M2G1+Ztj8/Au8aetvxsywExEReTnrxQP2lEyrvmqOJfFEROQ2PhmwK5agAp1iI+1N5+RsupxVJyIiIt/NsOvl8KEB/ggw++7HJSIici/ffQeyhBR40460z2GXs+mpWcyyExEReX3Arp+8L4behJbZdSIiciffDdj1s+r2sjg5ey5n0Z27whIREZFvZtj1Ma81Qrl+nYiI3IcBuz7axeksul4GR0RERL7ZJV7PsLNDPBERuRMDdr3xjFOneDaeIyIi8mL2/jUwB188w86AnYiI3MhnA3abJbjAWLeCo90YsBMREfl2hl0P2FkST0RE7uOzAfuFkninDLu9U7x+Vp2IiIi8uelcyRl2Np0jIiIjYMCul8U5Zdj1N2kiIiLy5qZzgWUoiWeGnYiI3Md3A3b9rLpzhl0viecadiIiIt+ew55ubzoXygw7ERG5DwN25ww7S+KJiIi8n7UMAbv9swBL4omIyJ18NmC3FbeGnSXxRERE3q9Mc9j1sW4siSciIvfx2YD9whp25y7x9gw7S+KJiIi8v0u85eIZdo51IyIid/LhgF0viS9mDTtL4omIiLxXrj6HvfiAPTMnD9m5+QU+GxAREbmD7wbs+ln1XOc17NqbMjPsREREXiovF8jPLTVg17PrZpMfwgLN1bl3REREBfhuwO4oiXfOsAc4usTn5dvctWdERERUVZx611wsYJfPBX5+ftW1Z0REREX4bsDuGOt2IcMeac+w22xAahaz7ERERF67fr2UOexsOEdEREbhswG7rZg17AFmE0ID/Au8WRMREZEXZthNFsCkvecXxoZzRERkFD4bsF9Yw+5UGsdO8URERN5Nf9/XK+2KoY93ZcM5IiJyN98N2IsZ61ZwFjs7xRMREXnvDPbiy+FFUjoz7EREZAw+HLAXLYkvONqNGXYiIiKvo7/v658DSsuwhzLDTkREHhSwT506FT169EB4eDjq1KmDa665Bnv27Lno47777ju0bt0aQUFB6NChA+bNmwcjjnUTUcH2knhm2ImIiHwzw8417ERE5IkB+7Jly/DAAw9g9erVWLhwIaxWK4YNG4b09PQSH7Ny5UqMGTMGd955JzZt2qSCfNm2b98OozWdK1gSzww7ERGR9wbsxY90K9h0jhl2IiJyL3N57jx//vwCX8+aNUtl2jds2IABAwYU+5i33noLl19+OR5//HH19UsvvaSC/XfeeQczZsyAkca6FSiJZ9M5IiIiL246F1SGpnPMsBMRkQcF7IUlJyery5o1a5Z4n1WrVmHy5MkFbhs+fDh+/vnnEh+TnZ2tNl1KSoq6lIy+bJWhPz4XZkhobrNmIdfpOcMDtREv59KyK/2zqpK+b0bex7LgcRgLj8NYeBzGPQ5PPxafVoYMO0viiYjI4wP2/Px8PPzww+jXrx/at29f4v1OnjyJunXrFrhNvpbbS1srP2XKlCK3L1iwACEhIXCFZX+vweUA/HIzMW/uXMDPT90ef0ou/bEv/jjmzTsKo5NqBW/A4zAWHoex8DiMdxwZGQUnjJAnNp27eIadJfFEROSxAbusZZd16CtWrHDtHgF46qmnCmTlJcPesGFDtV4+IiKiUs8tWRH5sDVg8HDAvox+xLDLHCXygbtOYfaBzbCERWHEiN4wKv04hg4dCovFcz9Q8DiMhcdhLDwO4x5HZmbB5VTkPU3n8vJtSMliSTwREXlwwD5p0iT89ttvWL58ORo0aFDqfWNiYpCYmFjgNvlabi9JYGCg2gqTD3qu+rBnCQ6/cB258uTqeq0ILXBPycz1iA+WrnxN3InHYSw8DmPhcRiLHENubq67d4MqKje71Ay79LCx2Qr2tSEiIvKILvE2m00F6z/99BOWLFmCJk2aXPQxffr0weLFiwvcJhkKud2t/C2An3/Bs+1qrJv25pzEpnNERETeR282W0LTOb1DfHigGRb/cn1MIiIicm+GXcrgZ8+ejV9++UXNYtfXoUdGRiI4WMtMjxs3DvXr11fr0MVDDz2EgQMH4o033sCVV16Jb775BuvXr8eHH34It7OEADmpgPVCaaNe/iZn2KUszt+krW0nIiIi78+w6w3nokKZXSciIvcr16nj999/X3WGHzRoEGJjYx3bnDlzHPeJj49HQkKC4+u+ffuqIF8C9E6dOuH7779XHeJLa1RXbfSz604Z9kh7hl3K4VLta9iIiIjIN7rEn0/XG85x/ToREXlYhl1K4i9m6dKlRW678cYb1WY45uCCHWMBBJhNCA3wR3pOHpIyrGw4Q0RE5ENd4vWSeL7/ExGREfj24ixHhr1gt1/9TZrr2ImIiHwrwy4n6wVHuhERkRH4dsCuv1k7rWF37gqrn2UnIiIiLwvYL9J0jiXxRERkBL4dsNtnr5cUsCfbz7ITERGRj6xht7/3c6QbEREZgW8H7OaiTedEVLC9JJ4ZdiIiIi/tEh9Y7Lf1935m2ImIyAh8O2CXsW6llsQzw05ERORV9Pd8vfFsiU3nmGEnIiL38/GAvYQMu14Sz6ZzREREPpZh51g3IiIyDt8O2B1j3Qpl2FkST0RE5J30yTB6H5tC2HSOiIiMxLcDdj3DXkJJPMe6ERER+U6G3Waz4Xw6m84REZFx+HbArmfYS5jDzjXsREREvtMlPiMnDzl5+ep6jVBm2ImIyP18O2B3ZNhLWMPOkngiIiLvYi05YNfL4S3+fggN8K/uPSMiIirCtwP2kjLswSyJJyIi8rUMu95wTirt/Pz8qnvPiIiIivDtgF1vOFMkwx7g6BKfl29zx54RERFRlQbsgaU0nOP6dSIiMgYfD9iDis2wR9oz7DYbkJrFLDsREZFXkDd2PWAvpku83rtGP3FPRETkbr4dsJuLz7AHmE2OtWt6eRwRERF5uDyn3jTFZNj1ca7MsBMRkVH4dsBewlg357PrXMdORETkJZzf7/WT9k70kW6cwU5EREbh2wF7CU3nnDvF6+vZiIiIyEtmsMMP8C+aRdff81kST0RERuHbAXsJY90KjnZjhp2IiMgr6CfopUN8MV3gWRJPRERG4+MBe0jJGfZge0k8M+xERETelWHXT9iX0HSOJfFERGQUvh2wmy+eYdffvImIiMh7Z7AXyLCHMmAnIiJj8O2A3XLxNewyi52IiIi8gLX0gP1Chp0l8UREZAy+HbCXlmFnSTwREZFPZdjZdI6IiIzGtwN25wy7zVbgW5H2s+sc60ZERL7o3XffRVxcHIKCgtCrVy+sXbu21PsnJSXhgQceQGxsLAIDA9GyZUvMmzcPhlzDXswM9ty8fKRm5arrzLATEZFRmOHLnM+wy1l3PYB3ajjDNexERORr5syZg8mTJ2PGjBkqWH/zzTcxfPhw7NmzB3Xq1Cly/5ycHAwdOlR97/vvv0f9+vVx5MgRREVFwVD0JXBO7/c65xP0kcEM2ImIyBh8O2B3fsO2Zhb4+sJYN5bEExGRb5k2bRruuusuTJgwQX0tgfvcuXMxc+ZMPPnkk0XuL7efO3cOK1euhMWivX9Kdt5wSsmw60vgIoLMMPv7dgEiEREZh28H7P4WwGQG8nMvrGuzi7KfXWdJPBER+RLJlm/YsAFPPfWU4zaTyYQhQ4Zg1apVxT7m119/RZ8+fVRJ/C+//ILatWvjlltuwRNPPAF/f/9iH5Odna02XUpKirq0Wq1qqwz98YWfxy8rTX3wyfcPRF6h751OyXScsK/sz3eVko7D0/A4jIXHYSw8DmMfh9UAx+PbAbswBwM5qVqG3YnecEa6xOfn22Ay+blpB4mIiKrPmTNnkJeXh7p16xa4Xb7evXt3sY85ePAglixZgrFjx6p16/v378f999+vPug8//zzxT5m6tSpmDJlSpHbFyxYgJCQEJccy8KFCwt83eT0RnQEkHD6PNYXWl+/7Zy8z/vDLyfDcGvvCx+Hp+JxGAuPw1h4HMY8joyMDHfvCgN2WIK0gL1Qhl1fvya96FKyrOwYS0REVIL8/Hy1fv3DDz9UGfVu3brh+PHj+O9//1tiwC4ZfFkn75xhb9iwIYYNG4aIiIhK7Y+cKJAPW7KuXi/RF6bVB4FjQGzDOIwYMaLAY9I3HAf27EBcbG2MGNEVRlDScXgaHoex8DiMhcdh7ONIsVd/uRMDdsmwFzPaLcBsQmiAP9Jz8pCUwYCdiIh8Q3R0tAq6ExMTC9wuX8fExBT7GOkMLx9snMvf27Rpg5MnT6oS+4CAou+h0kletsLkeVz1Ya/Ic+VrpY2mgBCYCv2M1Ow8dVkzLNBwHzZd+Zq4E4/DWHgcxsLjMOZxWAxwLOyqIhl2YS1a7qAH6VzHTkREvkKCa8mQL168uEAGXb6WderF6devnyqDl/vp9u7dqwL54oJ1I85h16fC6E1niYiIjIABu/6mXagk3vlN+zw7xRMRkQ+RUvWPPvoIn332GXbt2oX77rsP6enpjq7x48aNK9CUTr4vXeIfeughFahLR/lXX31VNaEzFL2arpQu8fpYVyIiIiNgSbw+yq1Q07mCo92YYSciIt8xevRonD59Gs8995wqa+/cuTPmz5/vaEQXHx+vOsfrZO35H3/8gUceeQQdO3ZUc9gleJcu8YbiyLAXncOun5yvwQw7EREZCAN2PWAvLsMebC+JZ4adiIh8zKRJk9RWnKVLlxa5TcrlV69eXQ17VjVz2C+UxDPDTkRExsGSePPFM+xcw05EROQFcjMLnqx3wpJ4IiIyIgbslouvYZcu8UREROThypRhZ0k8EREZBwN2R4a9mC7xLIknIiLyHno1XaEu8Tab7UKGPZQZdiIiMg4G7I6xbkUz7JEsiSciIvLCDHvBgD09Jw/WPJu6zqZzRERkJAzY9Qy7vq7Nib6OTS+TIyIiIg9Wwhz28+ladj3AbEKwxd8de0ZERFQsBuyOsW4lr2FPZkk8ERGR9wTsenWdnd6rRrLrfn5+7tgzIiKiYjFgdzSdK6ZLfDBL4omIiLw+w84O8UREZFAM2M2lZdi1N+7kTCvy87W1bURERORdXeL1gJ0d4omIyGgYsJeSYY+0Z9htNiAli1l2IiIi7+gSH1xCSTwz7EREZCwM2B0Z9qIBuzSfCQ3Qms9wFjsREZG3Z9gZsBMRkbEwYC9lrJvzmzfXsRMREXk4vZrOXHLTOSIiIiNhwF7KWDfn9Wz62XciIiLyQHm5QH5uwQkxdmw6R0RERsWAvZSxbgVHuzHDTkRE5LHy7OXwxZbEa+/xbDpHRERGw4DdcpEMe7C9JJ4ZdiIiIs/lfGK+SEk8M+xERGRMDNj1N+2LZNi5hp2IiMgLZrCbLIBJayhbpCQ+lBl2IiIyFgbsF8uw6wE7S+KJiIg8P2AvlF0XSel6STwz7ERE5OEB+/LlyzFy5EjUq1cPfn5++Pnnn0u9/9KlS9X9Cm8nT56EsTLsLIknIiLy+oBdnw5jZ83LR2q21oyOJfFEROTxAXt6ejo6deqEd999t1yP27NnDxISEhxbnTp1YKwMexZgsxX5diRL4omIiLw2w65X0Pn5AZHBLIknIiJjMZf3AVdccYXayksC9KioKBiO8xu3vJkXGvWin23XO8gSERGRB9J71ZTQcC4iyAJ/k5879oyIiMh1AXtFde7cGdnZ2Wjfvj1eeOEF9OvXr8T7yv1k06WkpKhLq9WqtsrQH3/heczQz6dbM1OLvCRhAdqbd1J6TqV/tisVPQ7PxOMwFh6HsfA4jHscnn4sPqmEDLt+Qr4GR7oREZEvBuyxsbGYMWMGunfvroLwjz/+GIMGDcKaNWvQtWvXYh8zdepUTJkypcjtCxYsQEhIiEv2a+HChY7rI+EPE/Kw5I+5yAqoWeB+JzPkv2acTknHvHnzYDTOx+HJeBzGwuMwFh6H8Y4jI0O9OZBHBuyFZ7BrGXY2nCMiIp8M2Fu1aqU2Xd++fXHgwAFMnz4dX3zxRbGPeeqppzB58uQCGfaGDRti2LBhiIiIqNT+SFZEPmwNHToUFot2Nt1vRzCQk4bLBvQFajYtcP8zadmYumUZMvP8cPnlV8BkkHK54o7DE/E4jIXHYSw8DuMeR2Zm8Y1KyROazgWXMIPdc/82iYjIe1VbSbyznj17YsWKFSV+PzAwUG2FyQc9V33YK/Bc8uadkwYLcuUbBe5XK1yb1Sr96DLzgKhAY72hu/I1cSceh7HwOIyFx2Escgy5uVpXcfIgudklZNj1knhm2ImIyHjcMod98+bNqlTeMMzBJY52CzCbEBqgBe2cxU5EROSh9Pf4ImvYWRJPRERelGFPS0vD/v37HV8fOnRIBeA1a9ZEo0aNVDn78ePH8fnnn6vvv/nmm2jSpAnatWuHrKwstYZ9yZIlaj26YVguMos9JADpOZkc7UZEROTxGfZCXeLT2XSOiIi8KGBfv349Lr30UsfX+lrz8ePHY9asWWrGenx8vOP7OTk5ePTRR1UQLw3jOnbsiEWLFhV4DrfT37z19W2FRIVYcDwp03EWnoiIiDxMbvEZ9nN6hj2UGXYiIvKCgF06vNtkQXcJJGh39s9//lNthmYJuUiGXTvrnsySeCIiIs/OsOtVdXZsOkdEREbmljXshmO5SIY9OKDAmzoRERF52xx2ZtiJiMh4GLBfpOmcc4ada9iJiIg8lLX4Oez6yXj9vZ6IiMhIGLCXqemcPWBnSTwREZGHZ9gvzGGXJX76ezsz7EREZEQM2J3fvPWGNIWwJJ6IiMj75rCnZuciN1/ry8OAnYiIjIgBe4EMe/Fr2CNZEk9EROTZ9JPyluAiI90CzSYEB/i7a8+IiIhKxIDduUt8CRl2/aw7S+KJiIi8J8Ouj2utyZFuRERkUAzYnTvGlpBhv7CGnSXxREREHknvU+PUJV4P2KNYDk9ERAbFgL3AWLeS1rCzJJ6IiMg7MuwXAvYLDefYIZ6IiIyJAXuBsW4lZdi1M+/JmVbk25vTEBERkWfPYdcz7Gw4R0RERsWAvUDTuYxivx1pz7DbbEBKFrPsREREHhuwW5wDdu09nTPYiYjIqBiwFxjrVnyGPcBsQqi9eywbzxEREXlHhl3vTcMMOxERGRUD9jKMdXMui+c6diIiIg+kv8cX6BLPDDsRERkbA/YyjHUT7BRPRETkDRl2pznszLATEZHBMWAvw1i3ggE7M+xERETeNIe9Rigz7EREZEwM2IUl+OIZ9mB7STwz7ERERJ5Fusbq7/HOXeLT9ZJ4ZtiJiMiYGLCXMcMeqWfYuYadiIjIs+Q5nWx36hLPkngiIjI6BuzOGfYSxrqJGiyJJyIi8kzWzCIn6XNy85Gek1fgPZ6IiMhoGLA7Z9hLGOsmWBJPRETk4evX4Qf4F3w/N/kBEUEM2ImIyJgYsBdYw56lrXMrBkviiYiIvGAGu59fgZFukcEWmCRqJyIiMiAG7M4BeylZdn19G0viiYiIPDVgL6ZDPNevExGRgTFgLzSTtcA6Nyecw05EROThAbvTCXr9/Vx/fyciIjIiBuzC3wyYzKVm2KOCWRJPRETkkazFZdi193Nm2ImIyMgYsBfOspeQYdfXsCdnWpGfX/w6dyIiIjJySXxwkZJ4zmAnIiIjY8BeeC5rSSXx9i7x0pMuJYtZdiIiIo/rEu+UYdd70nCkGxERGRkDdp1+1r2EkvgAswmhAf7qOhvPEREReZDczIJjXCXDnm5vOhfKDDsRERkXA/YyZtidy+a4jp2IiMgDM+z6e73TGnY2nSMiIiNjwF7cLPYSsFM8ERGRh89ht9Pfy9l0joiIjIwBexmbzhUM2JlhJyIi8uwu8RzrRkRExseAXaeXyZWWYbc3nmOGnYiIyLO7xF9oOscMOxERGRcD9iIZ9owS76KPduMadiIiIk8M2LUMu81mc7yXM2AnIiIjY8BepOlcyRl2ffQLS+KJiIg8MGC396tJycpFXr5NXWdJPBERGRkD9iJj3UpZw86SeCIiIo+fw66/jwdb/BFk0Ua2EhERGRED9sJd4kvJsLMknoiIyAPpDWXtXeL1kW565RwREZFRMWAvMtat5Ay7vs6NJfFERESemGEPKtQhnuvXiYjI2Biw6/TZrKVk2DmHnYiIyAPpJ+Pt7/WOGeyhzLATEZGxMWAvMtattDXsLIknIiLy2Ay7/b3+fLr2Ps4MOxERGR0D9iJj3TIvuoY9OdOKfHt3WSIiIvKUsW6FMuxcw05ERAbHgL3IWLeLd4m32WQkDLPsREREHkFf7mbvEn+h6Rwz7EREZGwM2IuMdSt5DXuA2YTQAG38CxvPEREReVqGXXuvZ9M5IiLyFAzYyzHWzfnNnevYiYjIm7377ruIi4tDUFAQevXqhbVr15bpcd988w38/PxwzTXXwHgBuz6HnWPdiIjIMzBgL8dYN8FO8URE5O3mzJmDyZMn4/nnn8fGjRvRqVMnDB8+HKdOnSr1cYcPH8Zjjz2G/v37w8hr2PUMO0viiYjI6Biwl2OsW8GAnRl2IiLyTtOmTcNdd92FCRMmoG3btpgxYwZCQkIwc+bMEh+Tl5eHsWPHYsqUKWjatCmM3CVefw/X39OJiIiMyuzuHfC4DLu98Rwz7ERE5I1ycnKwYcMGPPXUU47bTCYThgwZglWrVpX4uBdffBF16tTBnXfeib/++uuiPyc7O1ttupSUFHVptVrVVhn64/VLszUTfvK1fOyxWh0Z9vAAU6V/VlUqfByeisdhLDwOY+FxGPs4rAY4HgbsRTLspQfs+mg3rmEnIiJvdObMGZUtr1u3boHb5evdu3cX+5gVK1bgk08+webNm8v8c6ZOnaqy8YUtWLBAZfNdYeHChepyRFYa5N172d9rkGw5jIwc7ePPur+XYocHfBLSj8PT8TiMhcdhLDwOYx5HRkaGu3eFAXvRpnOlB+x6gxqWxBMREQGpqam47bbb8NFHHyE6OrrMj5MMvqyTd86wN2zYEMOGDUNERESl9kkyIvJha+jQobBYLDBvyVO3Dxw8HImoCaxZDpMfcN1VV8AkVwyq8HF4Kh6HsfA4jIXHYezjSLFXf7kTA/bCGfZSxroJlsQTEZE3k6Db398fiYmJBW6Xr2NiYorc/8CBA6rZ3MiRIx235efnq0uz2Yw9e/agWbNmRR4XGBiotsLkA5KrPuyp5/I3AfnaSXZLUBjSUm2OqS+BgZ7RdM6Vr4k78TiMhcdhLDwOYx6HxQDHwqZzOkvIhYDdpr2ZF4cl8URE5M0CAgLQrVs3LF68uEAALl/36dOnyP1bt26Nbdu2qXJ4fRs1ahQuvfRSdV2y5m7lfCLeEoTz6Ww4R0REnoMZdp29c6zjzV0vkS9EHwHDkngiIvJWUqo+fvx4dO/eHT179sSbb76J9PR01TVejBs3DvXr11fr0GVOe/v27Qs8PioqSl0Wvt0tnKe/mINwPkMrb+RINyIi8gTlzrAvX75clb3Vq1cPfn5++Pnnny/6mKVLl6Jr166q9K158+aYNWsWDMfsFKCXso6dc9iJiMjbjR49Gq+//jqee+45dO7cWWXK58+f72hEFx8fj4SEBHgEPcNusgAmf6cZ7MywExGRF2bY5Qx7p06dcMcdd+C666676P0PHTqEK6+8Evfeey+++uorVVI3ceJExMbGYvjw4TAMfzNgMgP5uaWuY48KZkk8ERF5v0mTJqmtpBPxpTHUiXn9Pd1ceAY7M+xEROSFAfsVV1yhtrKaMWMGmjRpgjfeeEN93aZNGzX+Zfr06cYK2PUse05qqRl2fQ17cqYV+fk2Q3eXJSIi8nmOgF1rcHc+nRl2IiLyHFW+hn3VqlUYMmRIgdskUH/44YdLfEx2drbadHo7fWmzX9nh9frji3sesyUIfjmpsGZK0F78zwk1awG69KU7l5aJSHvGvbqVdhyehMdhLDwOY+FxGPc4PP1YfDJgt/emOW/PsNcIZYadiIiMr8oD9pMnTzrWvOnkawnCMzMzERxctLmbNLGZMmVKkdsXLFiAkBB7N/dKkvl6hQ2x2hAqJxmWL8H50CMlPjbQ5I/sfD/8PG8hahffm67aFHccnojHYSw8DmPhcRjvODIyMty9G1TepnP2DLveg4ZN54iIyBMYskv8U089pTrU6iS4l7Eww4YNQ0RERKWeW7Ii8mFr6NChRebqmY++DJw5g749u8DW+JISn+PfO5fjRHIWOvfqh04NIuEOpR2HJ+FxGAuPw1h4HMY9DjnhTJ5WEq9n2FkST0REnqPKA/aYmBgkJiYWuE2+lsC7uOy6kG7yshXmyuH1xT6XvVzObMuVO5T4WCmjk4A9LSff7R8+XfmauBOPw1h4HMbC4zAWOYbc3Fx37waVVW52oQw7m84REZEXj3Urrz59+qjO8M4kQyG3G3a0WylN5wqOduMaRiIiIkPLzSzQJf5Chp0BOxEReWHAnpaWpuaxyqaPbZPrMpNVL2cfN26c4/4yzu3gwYP45z//id27d+O9997Dt99+i0ceeQSGYwkqW8AerL3JcxY7ERGRh2TYLUFquotMeREsiSciIq8M2NevX48uXbqoTchac7n+3HPPqa8TEhIcwbuQkW5z585VWXWZ3y7j3T7++GPjjXRzzrDrZ+MvMtqNs9iJiIgMTj8Jbw5CSpYV+TbtS5bEExGRV65hHzRoEGwy06wEs2bNKvYxmzZtguE5Muz2BjUl0M/KsySeiIjIc9aw6yPdQgP8EWCu8lWBRERElcZ3K2eWkDJl2FkST0RE5Hld4vX168yuExGRp2DA7sxctgw7S+KJiIg8LWAPvDCDPZTr14mIyDMwYC9mrNvFMux6Z1mWxBMREXlIwG4Jxvl0veEcM+xEROQZGLBXIMN+YawbS+KJiIgMTX9PV2vYWRJPRESehQF7sU3nMkq9W1QwS+KJiIg8qyQ+yFEZx5FuRETkKRiwFzvWrWxr2GWWq8x0JSIiIqN3iQ9ihp2IiDwOA/ZiM+xl6xIv0+1Ss3KrY8+IiIioIvS+NMywExGRB2LAXuxYt9Iz7DK7VWa4Cv1sPRERERk4w265kGFn0zkiIvIUDNgr0HTOuZyO69iJiIg8Yw37eXuGXW8eS0REZHQM2Csw1k1E6o3nmGEnIiLyiC7xjjnszLATEZGHYMBewQx7jVA9YGeGnYiIyPgZ9mCWxBMRkcdhwF5chv0iY92cG88xw05ERGT8gD3Hz4Isa766HmU/6U5ERGR0DNiLy7BfpOmc82g3rmEnIiIyMPt7ekqu1izWbPJDeKDZzTtFRERUNgzYi82wX3wNuz4ShiXxRERExu8Sn2I1OxrO+fn5uXmniIiIyoYBe7FN58rQJZ4l8URERMZnPwmfbDUVmPJCRETkCRiwOzM7Bew2W6l3ZUk8ERGR52TYz+f4F6iQIyIi8gQM2J1Z7GvYy5Blj3KMdWPATkREZEhy8t0+qvVcDjPsRETkeRiwF5dhL8M69hqhLIknIiIytLwL79HnsrV168ywExGRJ2HA7szfDJjMZQrYHRl2lsQTEREZuhxenMnSPvJwBjsREXkSBuylrWMvwxr25Ewr8vNLX+9OREREbmAvhwf8cCZTe69mSTwREXkSBuwVHO2md4mX5XGpWbnVsWdERERUkQy7OQhJmdp7NUviiYjIkzBgL6nx3EUy7AFmE0IDtI6z57mOnYiIyHj093JzoOO9mhl2IiLyJAzYSyqJv0iG3flNn+vYiYiIjBywBzmmujDDTkREnoQBewUz7CLSMdqNGXYiIiKj8dNL4i1Bjgy7PuWFiIjIEzBgLzHDnnHRu9YIvdB4joiIiIzZdM5mDnK8V0cxw05ERB6EAXtJGXbrxTPseuO58+nMsBMRERmOPcOeZwpQTWKd37uJiIg8AQP2Ese6XXwNuz7ajWvYiYiIjBuw55oC1WVYoFk1jSUiIvIUfNcqcazbxTPseuMavZENERERGYj95HsOtKw6y+GJiMjTMGAvKWAvQ4ZdL6tj0zkiIiLjZtiz/bT36xoc6UZERB6GAXth5rKvYWdJPBERkXH52d/Ls23a+zUz7ERE5GkYsFcqw86SeCIiIsPK0wL2THvAzgw7ERF5GgbsJWbYLx6w67NcOdaNiIjIuCXxGfnmAr1niIiIPAUD9hLHupU9w36ea9iJiIiMx14Sn56vl8Qzw05ERJ6FAXuJY93KvoZdMuz5+fYBr0RERGSokvi0PGbYiYjIMzFgL3GsW9m7xNtsQGpWblXvGREREVUgw55q9S+wlI2IiMhTMGAvsencxTPsAWYTQgO0DwEsiyciIjIWP/sa9pRc7b2aJfFERORpGLBXYqyb85s/R7sREREZsyQ+Sc+wsySeiIg8DAP2Sox1E5GO0W7MsBMRERmK/eT7+Rzt4w7HuhERkadhwF6JsW6iRuiFxnNERERkIPaS+PQ8vUs8M+xERORZGLBXoumcc+O58+nMsBMRERmKvVouGxZY/P0QFqh1iyciIvIUDNhLyrCXoemc82g3rmEnIiIyZoZdAnbpOePn5+fuPSIiIioXBuyFWULKVxKvB+wZDNiJiIiM2CU+CwFsOEdERB6JAXthlvJl2PWSeDadIyIiMmhJvE3LsBMREXkaBuyFmZ3msNtsF707S+KJiIiMXxLPDDsREXkiBuwlZdhFGbLsUY6xbgzYiYiIDMX+Pq6VxDPDTkREnocBe0kZ9jKuY68Rqn0A4Fg3IiIiYwbsetM5IiIiT8OAvTB/M2Aylzlg1zPs57mGnYiIyFj0pnM2Np0jIiIfCtjfffddxMXFISgoCL169cLatWtLvO+sWbPUGBXnTR7nEZ3iy1ASr69hlwx7fv7F17wTERFRNbDlwy/f6rSGnRl2IiLygYB9zpw5mDx5Mp5//nls3LgRnTp1wvDhw3Hq1KkSHxMREYGEhATHduTIEXjELPYyZdi1DwDSny41K7eq94yIiIjKwN8erItsBCCKGXYiIvKFgH3atGm46667MGHCBLRt2xYzZsxASEgIZs6cWeJjJKseExPj2OrWrQtvGe0WYDYhNMBfXWdZPBERkTGYbBfek1WG3d5zhoiIyGsD9pycHGzYsAFDhgy58AQmk/p61apVJT4uLS0NjRs3RsOGDXH11Vdjx44d8IjGc2XIsAu9kQ1HuxERERkrw26FP/LgzzXsRETkkezd1crmzJkzyMvLK5Ihl693795d7GNatWqlsu8dO3ZEcnIyXn/9dfTt21cF7Q0aNCj2MdnZ2WrTpaSkqEur1aq2ytAfX9rzmM2B8FMJ9lTYyvDzIoLMOA7gbGomrNZQVIeyHIcn4HEYC4/DWHgcxj0OTz8WX+Bvszoazgl2iSciIq8P2CuiT58+atNJsN6mTRt88MEHeOmll4p9zNSpUzFlypQity9YsECV37vCwoULS/zeJamZqAVg66olOLr34h/K8jKlUMGEpSvXIW1f9TaeK+04PAmPw1h4HMbC4zDecWRkZLh7N+giTPk5jnJ456kuREREXhuwR0dHw9/fH4mJiQVul69lbXpZWCwWdOnSBfv37y/xPk899ZRqbOecYZdy+mHDhqkGdpUhWRH5sDV06FC1L8Xxz/we2L0PXY5/hk7NYpDf+4ELo96KMT9lC/YmJyKuVTuM6N0I1aEsx+EJeBzGwuMwFh6HcY8jM7NsS6bI/SXxErCHB5lh9uckWyIi8vKAPSAgAN26dcPixYtxzTXXqNvy8/PV15MmTSrTc0hJ/bZt2zBixIgS7xMYGKi2wuSDnqs+7JX6XFe9AeTnwG/vfPj/+RL8984DrnkfqN2q2LvXCNP2NTU7r9o/jLryNXEnHoex8DiMhcdhLHIMubmcCmJ0JqeSeDacIyIiT1Xu082S+f7oo4/w2WefYdeuXbjvvvuQnp6uusaLcePGqQy57sUXX1Sl7AcPHlRj4G699VY11m3ixIkwrLA6wJhvgGtmAIGRwPENwIz+wN9vAfl5Re6ul9klZXBNIxERkRH420vic9QMds8/UURERL6p3GvYR48ejdOnT+O5557DyZMn0blzZ8yfP9/RiC4+Pl51jtedP39ejYGT+9aoUUNl6FeuXKlGwhmanx/QeQzQdCDw6z+A/QuBhc8Bu34DrnkPiG7huGsNvUs8x7oREREZqumclMSz4RwREXmqCi3okvJ3yZJLJ/c1a9agV69eju8tXboUs2bNcnw9ffp0x30laJ87d65aw+4xIuoBY78DRr0DBIQDx9YCMy4BVr3ryLZH2s/cc6wbERF5i3fffRdxcXEICgpS7/Nr164t8b5Sede/f391Yl42Gfda2v2rs+lcFgKYYSciIo/FDixlzbZ3vQ24fxXQ9FKZ9wb88TQw60pg89dolrwa7fwOw5SaAOQyy05ERJ5tzpw5agnc888/r5azderUCcOHD8epU6eKvb+crB8zZgz+/PNPrFq1ytEo9vhxGXrq5qZzNmbYiYjIc1X5WDevEtUQuO0nYMMsYMEzQPwqtXUDMFf6zp0D8DKAoCggtLZ9iwYsIVqQ79iytUur09d+JqDVFUDXcUBsR3cfKRER+bBp06ap5Wx6f5oZM2aoCrmZM2fiySefLHL/r776qsDXH3/8MX744QfVlFZ627i16ZzKsDNgJyIiz8SAvSLZ9u4TgGaXASumA+cPIzs5EUlnTqCWXwrMyAeykrTt7L7yPfe6j7QttrOW0e9wIxAUWVVHQkREVEROTg42bNhQoIGs9KaRMnfJnpeFzKmXMXg1a9Ys8T6yVE425xGuQh4nW2XI4/Wmc7KGPSLIVOnndAd9nz1x353xOIyFx2EsPA5jH4fVAMfDgL2iajQGRr6prianZqHXK4th8svH/n/1hSnzDJB2Ckg/rW2SRTcHA+ZAwGK/NAc5bYFA+hlg81fA7t+AhM3A3M3AH88A7a7Rsu6N+mgnC4iIiKrQmTNn1AhWvZmsTr7evXt3mZ7jiSeeQL169VSQX5KpU6diypQpRW6XyTIhISGorBZOJfGH9+zAvLPb4akWLlwIb8DjMBYeh7HwOIx5HBkZGe7eFQbsrhAVrJXa5dtMSDVFILJ2rRJntpeqxRAg/SywdQ6w8XPg9C5gy9faVqs50OU2IO4SIKQmEBAF2Gxlf+7sVCA1EZB19mmJQOZ5oEF3LZvPEwGVlpiShUyOZSYiwmuvvYZvvvlGrWuXhnUlkQy+rJN3zrDra98jIiIqtQ+SEYn//EdHSfygvj1xSfNa8DRyHPKhcejQobBYPLdxHo/DWHgcxsLjMPZxpNirv9yJAbsLBJhNCA3wR3pOHpIycxxd4ysktBbQ536g933a/PeNnwHbfgDO7gcWPe+4m/yEq/zMMB2oDYREa4+Ty5BagL8FSD2pbWn2y5y04n9eZEOgzUhta9gLMPlXfN991O/bEvCPbzYh0OSPLn3S0apelLt3iYiowqKjo+Hv74/ExMQCt8vXMTExpT729ddfVwH7okWL0LFj6f1YAgMD1VaYfEByxYc9R9M5WFA7ItijP0C66jVxNx6HsfA4jIXHYczjsBjgWBiwu4h0oE3PycT5DCsau+IkvmS9JQMu2/BXgR0/AVu/Bc4fATLOANYM+NtytYy5bGUhY+nCY7RNyvCPrASSjwKr39M2aZLXagTQZhTQZABgZpOei/ll83FM/nYL8vJtsOb54fbPNuD7+/qiflSwu3eNiKhCAgIC0K1bN9Uw7pprrlG35efnq69lrGtJ/vOf/+CVV17BH3/8ge7du8PdTPaAXTLsURzrRkREHooBu4tEBltwPCkTSRlVMNYtMFxbxy6bnTUjGX/O/R6X9uoIS3ayFsTLOni5zLMC4bEXgnO5HlYXCAwr+Lw5GcDBP4Fd/wfsmaett5eMvmyBkUDLYUBMR+3kgXSxh18x16WcXjZbwRJ9dd124bo8RrrfN+jhNVn879YfxT9/2KoOb1THWKzZdwIJyVm47eM1+PbePogOK5o5IiLyBFKqPn78eBV49+zZE2+++SbS09MdXeOl83v9+vXVOnTx73//G8899xxmz56tZrefPHlS3R4WFqY2t9Cbztks7BJPREQeiwG7i9QI1c7eJ2dWUydBSwgyA6K1NegVLdUICAFaX6ltEuQfXqEF79L4Tta5b/tO21xJSvZbDNNG2EmnfVPJ6xuN7Ks1R/Cvn7QGRrf0aoTnR7TC178cxYcHwnDwTDrGz1yLr+/ujYggZnWIyPOMHj0ap0+fVkG4BN+dO3fG/PnzHY3o4uPjVed43fvvv6+6y99www0FnkfmuL/wwgtwB5u8rwHINQUiJMA7ThQTkXFJs86KdBSXx5jNZmRlZann8FSeehwWi0UtAzMyBuwubjx3Pr0KMuzVQda9N7tU20a8DhxfD+yeq61/17Pntnz79fwLGXT9up5pdzSwc86+yyembODICiDj7IVGev4B8G/cD01yGgDJHYDopiXvX14ukJ0CZCUDeTlAjSZuK9mfueIQXvxtp7o+oV8cnruqLXJzc1EjEJh1ezfc8sk67DiRgomz1uPzO3siyGLsfwSIiIoj5e8llcBLQzlnhw8fhuHIe4WUxluC4MfmqkRURWw2mzqxmZSUVOHHS3+Qo0ePevS/VZ58HFFRUWrfjbrfDNhdRG80l1RdGfaqJFmThj21zZUk2xG/Gtg7H9jzO3DuAEwH/4RqS/TOF0CddkDdtkBWyoXgXN8KN80zWYA6rYGYTlqpvZTux7TXlg+UJDdHW7OfdETrBSCX1swLywb0LSK2xOeZsewAXvtdG2t078BmeOLyVgX+524SHYrP7uiJmz9YjbWHz+H+rzbig9u6weJ/IRNFRETVwJ5hNweypwgRVR09WK9Tp44aSVneoE96hKSlpanlQ86VS57GE4/DZrOpsW2nTp1SX8fGxsKIGLC7SFSwPWDP8IKAvSqz+E36a9vwV4Az+5C36zecX/M1aqXvg9+pHYBspbGEaOvhJYA/uU3bNjt9v2ZTe/DeAcjPvRCYy2XqCXuVQBkEhBXoA2Dzt2BHQjoijqfgZbMJ7RvUQCdbTfjN91dr8k02G1qeTIDfxtNoFxmLOVcG4cH/O4mVu7Px6LdmvDm6M0ymSpy1kyqGrCStT4HaTmvZI3OQfQss+VKOxZ//q1cZ+d3I78Pxt3ZY69PQdJB2QslD3rR8hqMiiLydn73pnDmAATsRVQ0p/daD9Vq1alU40JUlRTIG01MCXW86juBg7T1Cgnb5PRoRP8W7iN7QptrWsHuD6BbI7z0Jf59rihGX9oHl8DJtDF1Q5IUtMMJ+PQoIitCCfvnAnRQPnNwKJGy9cCkB+bmD2rbz5+J/pjkYiGoE1GgMRDUGAkK19fopJ+yj8BK07L6cEDi7T9vshf3tZdP/j5GVAlpPJUWK3tvIlQRt7m9bAIvlRn8gdXcwzr1WC7XqNoBfSE0tmDOZAT/7pXwtJyGcr2dKcH76QjNB2ewfPitEgncJ3OV4pXpALp2/lqaEUY3gF94AoVkntSUMVTHGIj9Pe31lfywe8iFa/t5kKYdUZyQfK3gSSC7lb9GaUfxjZdRi88FA8yFaz4bQ6OreexLyOzq0HDj0l3Z55wIgqqG794qqqUt8QFCou3eFiLyUvmZdMuvkuULsvz/5fRpxPTsDdheXxJ+vii7xviC4BtDxxrLdV7JjEnDLJvPjdRLUJmzRsu6JOwBLkBaU14izXzbWRtddLLuWnWYP3k/AlpKAReu2YcOh0zAhH5e1ikb3RpH2tfv5WgBqy0denhVH921Ho5pBMEmQnXZaOxGQl41wv0yE5xwDjh5DpckJDAn6JBCU7Llk2XOztADb+dIq17MudOpX38vSTgBc5B+EIfKoXU9oFQbqxEYjbZPXTu9lUKCfQX7B10OWNKilDEkXLjPtSxtkooFOTsToVQxhThMN9OkGciJBTlxIsJx5Dsg4Z79+/sJ1eW4pe9V7Kdj3w2zLw/CsLJj3TNZukxME8jcmJ0yCo4BguazhdFsN7WSOnPRJPq4F5nqAnnLc/lqWxg+IqHfh70xeg0PLtNd76xxtk/vU66wF77LV787Kh6oi//+q4HyZFqDLiRVnh/8COt/irr2jamKyaR+kA+3ZEyKiqmLUtc/kHb8/flr00JL4NYfO4VBqtfwozxGqZzMHV+55ZPxdYHPk12yG53/dgS/2ayOJXrq6Hbr3iSv2IflWK7Zkz0P9ESNg0jPTEkBmp+DXlZvxxcL1iPZLxi0dwtG/eQ0tsFXBvlzmFvxaAkwJZiVAlsBcjktdr6WdhCgr+fkS0OekaxUDciJCXU/VLtXXcnuqVllw/ghsSUeQd/YgzDIOSYJX2eJXoUro/QlOaz0BXEn+2VWvVK7TjXIslSEnFSLr209g2ANz/YRQZAPtBErhngnH1gL7F2mbnEg6sUnblv8XCAjXnks/QSFVDs6XsgXVgjkvAzizF8g8c6EKRC6lGkX/OkuvWpBlEMHavkgFg17J4PhekNNtgUXvqy+j8A8oeln4NnlseUve1NKOQid05O/PcZIpUzvpJL0l1G32r/Oytb4V/vpm3x/n61KZcmqnFqDL6+VMqlnqdwWaDADi+gMNe1Xub4E8gr89wx4Y7KaxckRERC7AgN1FaoRWX0n8r1tO4B9fb1K/vi3WjfjXlW3RvE4pzdao3PLybXj6x22Ys146XQKvXdcBo3s0Kt+TyAODIjHqsoE4ZqqP/8zfg9+3AP9u3gGje5XzuSpCfr4KrAK1LHIZ5FqtmDd3LkYM6gVL2gl7ybe97Fuy2hIUFbvZpwJIYBTkvIwhUsto69f1pQ0SiBUOOlMTLwSjciknE1QGvJaWEZdjUNlwp0uVGQ902gdtf6x5+fhrxd/oP2AgLGaLFvzpWXrJ0Oub4+tzFxoQSvCttobaZUR9LXteOCC/GJliEHeJtg15QTuuA0u04F0u5edepG+DnPq5Uq5shTHpwb/0llCXTtcloJcAPDMJ5qxkXJFyCubNmWXvI1EpflozShWgDwAa9ym9ISV5JbNNq3hjqSoRUdWKi4vDQw89hAkTJrh7V7wSA3YXZ9iruiR+Y/x5PPbdFsfXf+45g+X7/sKYng3x8JCWiA4rZ1BBReTm5ePx77fip03HIX3i3ripE67t0qBSz3n/oObqZM4Hyw7iqR+3ITzIghEdjNmJUgW+ktWPigUadKuanyHBrwTy0um/KlitSA2OB2q3rpq1+BUhJwOkDFs2qaY4vcdexWA/aSFLKApf2kvxbUGR8CuybEAuJRsfq50McSyHsGennbPWBS6dtpK+VkstJLNt1bLbUi2gLrMvLLMQ+uPk5MNFKh4KDGGUjLh+EkcCaecMv14l4FwRIH8vUoki+6X2KafQdfulnGSRIL1x3zKfpCLvZbaXxAeHcA07EVFhgwYNQufOnfHmm29W+rnWrVunmrfJmGNyPQbsLl7DLkFZfr6tch3BS3DsfAbu/nw9cnLzMbh1bfQMTMC67Fgs2n0aX66Ox8+bTuC+Qc1w5yVNOPu7giQz+/A3mzF3WwLMJj+8dXMXXNnRNYH1k5e3RkqmFV+vPYqHvtmEsEAzBrSs7ZLnJg8jzQVlhKFsJbHZYE07iz8WLcHwq66FxQgnHqSkXQJnPbiXZnvqBIGcDMi88LV+XbLtQZHItYRh+Zot6D/sKljCa2tBuMHXi5HnC4B2Aj0slCXxREQVGXkmXfDN5ouHi7Vr11Zd4lNSUqpl33yN5/TcN7io4ADH59nULNefXUrNsuLOWetxJi0HbWMj8MYNHRATArw/tgu+vqs32tePQFp2Lv77xx4MfmMZft50XJ04oLLLzs1Tc9MlWLf4++G9sV1dFqzrDS1evqaDek5rng33fLEBG46UnpkkH2ZfUpFnCjTWPsm6cTVZoLa2jl+qJOp10bLa0kxPGkF2vAnodrt22XI4bA16IjW4vlYNINl0ButUDQLsGXYG7EREBd1+++1YtmwZ3nrrLfX5VLZZs2apy99//x3dunVDYGAgVqxYgQMHDuDqq69G3bp11Yz1Hj16YNGiRUVK4uW5dPI8H3/8Ma699lq1LKlFixb49ddfy7RvcpLgzjvvRJMmTVTWvlWrVgWeWzdz5ky0a9dO7afMT580aZLjezJq75577lH7LGPm2rdvj99++w2eigG7iwSYTQgN0LLaSZk5Ll9PLWvW9ySmok54ID65vTtCAy+c7erTrBZ+feASTB/dCfUig3A8KRMPz9mMa977G2sOnnXpvnirLGueCqAX7kxUv8sPx3XHsHYxLv85/iY/TL+pMwa2rI1Max4mfLoWuxJ4NpKIyKVsNgRAC9jDw9m/gIiqNzOdkZNbri0zJ6/cjyluk59dFhIA9+nTB3fddRcSEhLU1rChNu70ySefxGuvvYZdu3ahY8eOSEtLw4gRI7B48WJs2rQJl19+OUaOHIn4+PhSf8aUKVNw0003YevWrerxY8eOxblz5y66b5Kpb9CgAb777jvs3LkTzz33HJ5++ml8++23jvu8//77eOCBB3D33Xdj27Zt6mRA8+bNHY+/4oor8Pfff+PLL79UzyHHY8RxbWXFkngXigoJQHpOJs5nWNG4luue95W5u/DnntMIspjw8fjuiI0Mdsx91EkJvqyzvqJ9LD5ZcQjvLz2ArceSMfrD1RjWti6evKI1mtZmlqE48g/cXZ+vx9/7zyLY4q9e437Nq25etpwQeP/Wrhj3yVqsP3Iet32yFt/f2wdx0VxnSUTkCrk5WbD4aR9cI8P43kdE1UcSMm2f+8MtP3vni8MREnDx8C4yMhIBAQEq+x0ToyWodu/Wpva8+OKLGDp0qOO+NWvWRKdOnRxfv/TSS/jpp59UkOyc1S4uiz9mzBh1/dVXX8Xbb7+NtWvXqoC/NLIEUIJ9XZMmTbBq1SoVsMsJAPHyyy/j0UcfVY3udJL5F5L9l58jJxxatmypbmvatCk8GTPsLhTpGO3mugz7l6uPYObfh9T1aTd1RscGUaXeX9auP3Bpc/z52CCM7dVIZXQX7EzEsOnL8cKvO3AunXPinckygttnrlPBulRIfHZHzyoN1nXyj+knt/dAm9gInEnLxq2frMHJ5IvN+vZ8cuZ327Fk7EvkTEKjOJ+ew38XyOukpKU5rkeEM2AnIiqr7t27F/haMuyPPfYY2rRpg6ioKFUWL8HwxTLskp3XhYaGIiIiAqdOnSrTPrz77ruqLF/WxoeFheHDDz90/Dx5jhMnTmDw4OLHOG/evFll6PVg3Rsww+5CNUIvNJ5zhRX7zqg54OLx4a3K1VW8dnggXrm2A27vG4epv+/Gkt2nMGvlYfyw8RgevKw5xvWJ8/nGdPJ7uv3TtdgUn4TwILMK1rs2qlGtJ3g+v6MnbpyxEofPZuC2T9bg23v6OEYEepuVB85g2oK9qqpA9GxSExP6xmFo27ow+/PcoTt6Nry1aB8+WH5QnUjp1aQWRnSMxeXtYtS/H0SeLDk1FXXt180BwW7eGyLyJVKtKZnuspIS7tSUVIRHhMNkMlX6Z1eWBNfOJFhfuHAhXn/9dVV2LuvKb7jhBuTklH6yv3CzXFnXLsd6Md988436mW+88YYq2w8PD8d///tfrFmzRn1ffn5pLvZ9T8SAvQoaz0nGqrL2n0rFfV9tUOvXr+tSH/cPalah52lRNxwzb++Bv/efwctzd6n10q/O243PVx3BE5e3xlUdY9X/QL5Gfke3zVyD7cdTEBViwRd39EKHBpHVvh8SGH1xZy/cOGMV9p1KUycQvrqrt+og7y3WHz6HNxbsxSp7PwVZEiB/12sPnVOb9F24rU8cbu7R0GtPVhjN5qNJePy7LepvTie/H9me/2W7OplyZcd6DN7JY6Wlp6vLbAQg0Aff44jIfeRzdVnK0nUSxOYG+KvHVDZgLw8piZcGbxcja8GlvF0ayOkZ98OHD1fZfsnP69u3L+6//37HbQcOHHBclwBemtzJmvpLL7202Mz+sWPHsHfvXq/JsntPVGCg0W5JlcywS3nqHbPWq27z3RvXwNTrO1Q6qJYy798evAQ/bjyG1xfswbHzmXjw601qvfuzV7VBt8a+M7NYlaB/vAa7T6aiVmgAvpzYS5Wmu0vDmiH4cqJk2ldhy7FkNbpPTrJ4egXE1mNJKlBftve0+lo674/p2Ugt2ci32fDV6njMXhuPE8lZ+Pf83Xhz0V5c26U+xveNc+vv42IOnE7DvK0JWLr3NOpFBWPy0JZo4iH9B6S54puL9uHD5QcgQySiwwLx8jXt1eSJedsTMG9bgup9sfrgObU5gvcOsRjePgZ1woPcfQhE5QrYc/wCwFNORERFSdArWWsJvqXsvKTst3R4//HHH1WjOYlHnn322TJlyitKft7nn3+OP/74Q61f/+KLL9Scd7mue+GFF3DvvfeiTp06qsFcamqqCvQffPBBDBw4EAMGDMD111+PadOmqaoAWZ8v+36x9fNGxYDdhaIca9itlSpTvfeLDYg/l4FGNUPwwW3dEGh2TeAm69lv7N5QjRX7+K9DmLHsgMq0Xf/+KozoEKMy7o1reUbgUVGJKVm45aPVOHA6XXXcn31XLzSv4/4OwrIPUpI/5sPVWHngrJoKIGPlPLFUXKo4pi3cqzru6393N3VvgEmXtUD9qAtlSo8Nb4VJlzXH/205gU//PoydCSn4Zt1RtfVuWhO3922iyuXl8e4mFS9zt55UAa1Ma9DJWL752xPU0hM5Pr2PhRFtij+Px77bov72xdWd6+GFke0cVQ33DmymtqPnMtRxyrbFKXh/7tcd6BknmfdYXM7gnQwuPV2rHsn1Y9UOEVFxpOx8/PjxaNu2LTIzM/Hpp58Wez8Jeu+44w6V9Y6OjsYTTzxRpfPWZRybdKMfPXq0CrLHjBmjsu0ybk4n+52VlYXp06er45D9kjJ93Q8//KBul8emp6eroF06xXsqBuwuVCMkoFJr2GUd6dM/bsfaw+fUmuqZt3dHrTDX5wak5OYfg1uoEuTpi/ZizrqjmLftpAqwZG27rHGXjvfe5kRSpgrWZb14bGQQZt/V21CZUWko+PH4Hhj/6VrVKPCJH7bhvzd0VBMAPIEEtdMX7cPcrQnqa9nta7rUx0ODW5R4IkiqCOQk0g3dGqi17bP+Poz5O046gkQJ8Mf1aYzRPRpW+9/k3kQJ0rXA1bl03GzywyUtojGkTV0s3pWoJjh89Nch/LDxuMq2y/9XRjrRIln16Qv34qO/DqqsuupvcU37EscWSsXHPQObqU2C99+3J6jXQYL3NYfOqU16azB4JyPLyNBOTOX5M79ORFQcKReX7uvOpPS9uEz8kiVLCtwmI9WcSZZesu56IF/ceDmZjV4WMlddTh4UPoEwderUIoG9bMWRzvYyp91bMGCvgpL48xXsEv/+sgOqKZxkFN+9pWuVZ37rRARh6nUdVQmyrGtfvve0KpH/fsMxFdDf1ruxWm/sDSTwGPPRarUUoEGNYHx9V28VmBhNn2a11O/+3i83qL+FiGAznruqraH7DBw5m66al/28+bgKCIX0Rnh4SEs0r1O27sxyfD3iaqpNTqzIdISv18bjeFKmapooJ5ZkbKFkslvFVM3/F/LmItlzKXeft/0k9jsF6VLO379FbdX4cWibuo7/12/t3RhL95xS/SHk/s/8vB1frDqCZ69qq4J6d5MKgMe/34KD9qy6LDl4fmTbMp/8kP9H7h7QTG2O4H3bSWw5mlQgeJffm5TNXyHBewSDd3K/rEztbz6fATsREXk4BuwGKYmXstr/zN+jrr8wqh0GtKyN6tI6JkJ1K5e1xlPn7VJru1/6bSc+X3VY607fPtZjsrzFOXQmXWXWE5KzEFcrRGXWZe2xUUkZ+Os3dsQjc7aoUnFpZvjQkBYwmmPnM/DOkv34bsMx1URODGtbF48MbVmpNejyu/nn5a3VSaNf7eXyUmYvAbxsfZrWwu394lSGu7Ll8hKk70pI1UrAtyc4AlsR4G/CgJbRKkgfLEF6CeXug1rVUT0iZq+JVycWJOiXMX1D2tTB0yPaoGntMLdk1d9YsAcfrzgEOcktyz9evbYDhrTV+2aXn3PwLr/737edxNxtCWpZjd5A8IX/24EejbXMO4N3cqfszEx1afPn3yARkZHI2vMvv/yy2O/deuutmDFjRrXvk9ExYHchfS1oeUviZS71w3M2q+uSQZTMtjsMbFkblzSPxnfrj+KNhXtx5GwGJs3ehBZ19uHBwS1UBs0I64nLKiXLqrKdUgosJ1Ek2zt7Yi+PCCIkm5ycYcUL/7dTBYGSaZ/Q70KzDXf3AZBA/Zt18bDmaYH6pa1qY/LQVi7ttC/l8jd1b4gbuzXAusPnMWvlIczfftLRzVwqJVS5fPdGjox3WYP0HSdSHOu0ZYmETipK5P8D+Vu/rE0dRASV7Xkt/iZVqSLrwt9avE/93S3adQpL95xWt8vJh+pa3y5d+f/5/VYcPKOdfLiua308f1W7cr1GF9OgRgjuGtBUbUWC98Pn1KYH79IfY0hr91cbkG/JyrL/f21mhp2IyEhefPFFtb68ODKrnYpiwF4FGfbylMQnJGfizs/WIcuaj0GtauOZK9vAnSQgv7lnI4zsVE8FulIiL+t3pQnaW4v24sHLWqjvGTlwly7wM1ccUkFTanauuk26YH9+Z0/VFdtT3N6vCZIzc1XAPuX/dqqA77quDdz6ur6/9IAqV8/O1bqD9mteSwXq3RpX3fx6KZeXTuWyHXcql5flDbKUY/rCfbi2a311sqtl3fASg3QZ4SdBpZR1y8koXaDZpP7fk0z6Za3rILyMQXpxpNT8+ZHtMLZXY7w6bxeW7D6l/h+S6QyTh7XCmCpc356Zk6cmQMz8W8uq143QsupSHVCVnIN3+f38vk3K5hOwKf5C8D7lN6B1pAkNOiajWxMG71T1rPaA3WQxbjUVEZEvks7uslHZMWB3IT2DJRn2/HzbRcvI07Nzcees9TiVmo2WdcPwvzFdDNOsKjTQrNYg33FJE9UITIIO6S4tlQBvL96nRnNJNtEo+yskWPho+UEVzOkBZYs6Ybj/0mYY2dFY+1pW/xjcXP09SRD2+PdbcTYtB+3qR6hmbDGRQS6bIFCapIwcfLD8ID5beRgZOdq8zh5xNVSgLmvuq5Mct0wzkEZ2v2w+rsrlZQmHlKPLJicQpLt8/2Y1VNAqjdIW7jqtyt2PntNKZEWQxYRLW9VRQfqlreu4fO69VHPIaD5ZZvLybzvVSa9n1fr2w2p9u6yHd6V19qy6LP8Q0sTv2SvbujSrXtbfz8T+TdWmB+9SxbAxPgm7kky4/gNZKlBXNedrW49n0anqWLO1/99NAcavqCIiIioNA3YXkrXGQgIFmaFe2odlCegl+JVRVtFhAfhkfI9KZfaqipQESznvhH5x+NxeXi6lto9+twVvL9ECd2lkJSXB7iLNvmRE3c+bjiPXvpa6U4NI3H9pc9UgzJPX30t2WaoupLxfmgG+Mm9Xge9Lx29Z810vMki7jApG/agL12XOfEUb1snP/OSvQ6paQa9UkNdVMsUDWkS7tRGelMuP7tFIlcxL4zM5qbRg50n8vf+s2qRcPiPDH+dWr3E8JtjirzLoV3SIUcG6nJSqalJe3++h/uokkoy625uYhts+WYvBrevg6SvboFkl17dLVv0/f+zGrJWH1b87MaqRZAd1EsLdnIP3/SeT8fRXy7H+jAmLdiWqTZYdPDK0hSHGKpL3aVcnADgKBAQZr7koERFReTBgdyFZ/xoa4I/0nDwkZeaUGrD/+4/daoyaPOaD27obsmO5MzmZIMG5rMfV14VLWbFk9f4ngfug5qpcuzq7ym8/noz3lu7H79tPqmBF9G1WS+2nXBq5s3p5yAmH167rgEY1Q1QmVTKX0kldllGcTs1W25ajxT9Wfh/Owbwe0MdG6l8HqTF/zrLzgBnLDuLjv484+jFIE7lHh7bE4DZ1DPW6yr70blpLbbKW+ovVR/DN2qOqXB7wQ0iAFqRLJl3K3gsfa3WQyo7b+sRhVKf6an27NHNcvPuUyr7LGEWpFqhIJlyavEkHeL28X2bd/0uy6gacBd+4VgjGNs/Hi2P6452lB/Hb1gTH8oSrO2uj/+IMNGKRPN+wlhEqYA8N4d8VERF5NgbsLiZrWNNzMnE+w4rGJVQLf7vuKD5YdlBdlznbVbn+19WkdPi+Qc0wvm9jtZb4w+UHVanxkz9uw/+W7Ffl5zd2a1hlgbusRZZA5d2lB9QYOufO6vcPaoYujTzntSxv0CeVDs6vg/yNSeDu2JKzHMG8bLLUIic3XzVVc26sVliNEIsjmK8ZYsbczf5Iy93vWFIgXd8vbxdj+EoFWUv91BVt8PDglvhj+wls27IJD900GBGhxiiJlaD8uZFtMbZ3I7w6d5cK2mWpw4+bjqkS8Vt6NirTso2MnFw1UeKzVVpWPTZSy6pLt3qja1Y7FO/c0hUPXJqiZsMv2JmInzYdV9MApLngpMuaq98jUaXlZmuXbDpHREQejgG7i0l2S4ImWfdbnFUHzuLpn7ap65JVkuySJ5JMpYx3uq13HL5ac0StcZbj/tdP2/Hukv0qqL+pR0OXrbGWAFU6br/7536sP3Je3Sbx46hO9XDfoOZVNpvbqCSzXDM0QG3t6xffmV2CdenorgXzEsRrAX1C0oXradm5KvCXTTqn258djWuGqEDd6A0GixMc4I8rO8TA76hNXTcaKYP/5PYe6oTTy3N3qjL5537ZoSpXnrmqrSqjL8nqg2dVVUv8Oe0EzM09GqrS+rJ2szcKqdj4cFx3bD2WpJYKyP/b36w7ih82HsOYno1UlUxdD5jmQAZmzVIXNjObzhERkWdjwO5iNUIvNJ4rTBpC3ffVBrXOWgKhhw04W7u8JCCSNaq39m6s1unKWnLJ9D77yw68++cBFbiP7tFQrTmuCJnvLU2r3lt6QM3i1udj39i9Ae4Z0AyNajEbVxKpcpClFqUtt5B16hey9Fk4fi4dqSf241+39kVwEDNTVWlAy9qY16w/vl53FNMW7FGN6cbPXKtG5ElpuzSuc25QOX2eZNWPqK9lmcPU6zuWGtx7go4NojBrQk9sOHIObyzYi5UHzqpeGXPWHVXjLe8d1MyjJjuQgeRpATsz7EREVSMuLg4PP/yw2qhqMWCvosZz59MLZtgl437nrHVqHnjnhlGqFN5Ia4ErSwJymRMu2bFv1x9V478SkrPw/K8SuO/HvQOb4ZZejcocuEt2+KdNx9Raar3ztfQHGNu7MSZe0sQjZql7AsnMRsRY0DpG69httVoxb94+j+yo74nU+vbejVWlyP8W71PN4/7ccxp/7VuuToI9MLAJ9iX74b/vrrKvy4f6f+zpEa0N2aSyoro1ronZd/XGygNnMG3BXlVF8/GKQ5i9Nl6N67t7QFO13Iio/CXxfK8gIiLPxoDdxfTmUUlOGXZrXj7u+3Kj6q4unZM/HNetwhlno5PjkkZaklX/bv0xFbhL6fWLv+1UWfJ7BzZVgXtJzb9kfe7Xa4+q8WwnU7QMSVSIBRP6NlHr5vmhnbx1KY2Uw8v/GzJbXrqoS/D+3YajSM+Wfysy1b8dr13fweUj4Yykb7No9Lm3FpbvO4M3FuzB1mPSWPKAWi5wZ/8masykp5X/k3v42UviGbATEZGnYxrNxaLsHZolk66vvZb5y6sOnlUZ4o/Hd0edcO//ACFr1yVD+Odjg1RDLBmzdSYtGy/P3YX+//4THyw7oMp8dbKEQDKM/V5bgpd+26mC9boRgWqk2d9PXIaHhrRgsE5er2ntMPVvxJd39kKruuFIl5b9klXv0QB/PDLAq4N1nVQeSan/Lw/0w0fjuqN1TLgaK/jmon3q3w6ZDCEn9ohKxZJ4IqISffjhh6hXrx7y8/ML3H711VfjjjvuwIEDB9T1unXrIiwsDD169MCiRYsq/POmTZuGDh06IDQ0FA0bNsT999+PtLS0Avf5+++/MWjQIISEhKBGjRoYPnw4zp/X+lbJfv7nP/9B8+bNERgYiEaNGuGVV16Br2CG3cVq2INKfQ37JysOqWZK0rfrf7d0Uc2WfG0dtZTw3tCtAX7aeBzv/LlfNcya+vtu1ahuQp9G2HzEhKffWO4ITmQElJTQX9e1vsua1hF5kktaRGPuPy7B3C3HcWjnRjwwqi0sFt/651oCd5n+IDPr521PUF3lD5xOVx3yZ644pP6NkJOC3lqtRJXEDDsRuYuMcLGWPJ2nCAma5f45/jLLt3I/2xIib6AXvduNN96IBx98EH/++ScGDx6sbjt37hzmz5+PefPmqWB6xIgRKiiWAPnzzz/HyJEjsWfPHhUsl5fJZMLbb7+NJk2a4ODBgypg/+c//4n33ntPfX/z5s1qP+RkwVtvvQWz2az2LS9Piw2eeuopfPTRR5g+fTouueQSJCQkYPfu3fAVvvUJsBpL4s9n5GDRzkS8Mm+X+vqZK9vistZ14ass/ibVNf7arvXxy+YTeGfJPjVq7I1FMj5M/nHKU5k0aVJ3ZYdYrqEmnyf/D4zoEIN5R+HTZJzgVR3r4Yr2sfhl83E1y15mz0u1zkd/HcSkS5u7dCIFedcadhsDdiKqbhJ8v1qvzHeXT7xRrvrZT58AAkIvejfJYF9xxRWYPXu2I2D//vvvER0djUsvvVQF2J06dXLc/6WXXsJPP/2EX3/9FZMmTSr3bjk3ppNmdS+//DLuvfdeR8Au2fPu3bs7vhbt2rVTl6mpqSqIf+eddzB+/Hh1W7NmzVTg7isYFVVRSfzuhFT845tN6iTb2F6NMKFfnLt3zTCBu2TbF00eiOmjO6Fjgwi0iMjHB7d2we8P9Vdj7hisE1FhMl7wuq7avx3/vr6DWtOfmJKtJlJc9voyzFkXr/qFECksiSciKtXYsWPxww8/IDtbO8H51Vdf4eabb1bBumTYH3vsMbRp0wZRUVGqLH7Xrl2Ij4+v0M+Scno5MVC/fn2Eh4fjtttuw9mzZ5GRkVEgw16cXbt2qX0s6fu+gBl2F6sRqpXE6w3TLmkejRdGtfOqjvCuIEH5tV0a4Kr2dVXpzWWtavM1IqIynfQb3aMRrulSX41/e2fJftXY8okftqkml9/d2xe1wxmk+TyWxBORu0hZumS6y0jWZ6ekpiIiPFwFy5X+2WUkJe7Sa2vu3Llqjfpff/2lSs6FBOsLFy7E66+/rtaNBwcH44YbbkBOTsEpWGVx+PBhXHXVVbjvvvtUiX3NmjWxYsUK3Hnnner5ZM26PH9Jgkv5nq9gwF5FGXbRrHYo3h3bVX3AJCIi15ESeJlIcVP3hvhy9REVrEugHh3G5pQE+HGsGxG5iySgylCWXmANuyVPe0xlA/ZyCAoKwnXXXacy6/v370erVq3QtWtXRwO422+/Hddee636WjLuEnhXxIYNG9RJiTfeeMNxQuLbb78tcJ+OHTti8eLFmDJlSpHHt2jRQgXt8v2JEyfCFzFgd7F6UcEIDzTDYjZh5u091LgmIiKqGtJ0bmL/pqq55bn0HFbqkCY3U7u0MGAnIiqtLF6y3zt27MCtt95aIEj+8ccfVRZe3lefffbZIh3ly0oy9FarFf/73//U88nJgBkzZhS4jzSVky7y0oxO1rYHBASopnPSHC86OhpPPPGEalInt/fr1w+nT59W+yxZel/A1K+LhQaasejRgVjy6EA0rlWOs2tERFSpf3sb1ix7KSB5t7xR72Fls8dhq93W3btCRGRYl112mSpRl+7vt9xyS4ExbNKYrm/fvirIlhFreva9vKR5nTzfv//9b7Rv315l9KdOnVrgPi1btsSCBQuwZcsW9OzZE3369MEvv/yiusULOWHw6KOP4rnnnlPr6kePHo1Tp07BVzDDXgXqRvCMPhERkbvY6nfD6YhEINhlvZeJiLyOlKifOFF0vb10cl+yZEmB2x544IECX5enRP6RRx5RmzNpPOds4MCBKvte0n7+61//UpsvqlCG/d1331W/SFn70KtXL6xdu7bU+3/33Xdo3bq1ur+UO0iTMSIiIiIiIiJyYcA+Z84cTJ48Gc8//zw2btyoyhykTKKksoSVK1dizJgxao3Bpk2bcM0116ht+/bt5f3RREREREREZBBS4i5j3yIiItCgQQN1KV/Lps9Sp2ouiZc1CHfddRcmTJigvpamATIOYObMmXjyySeL3F8G3V9++eV4/PHH1dcvvfSSGhPwzjvvFGk4QERERERERJ5h1KhRquJamtJJN3kJ1PVu8BYLm29Xe8Aus/KkNb908tPJL2TIkCFYtWpVsY+R2yUj70wy8j///HNF95mIiIiIiIjcLDw8XG1qnnxKisqwV3qePFU8YD9z5gzy8vJQt27dArfL17t37y72MSdPniz2/nJ7SbKzs9Wmk1++kJEAslWG/vjKPo+78TiMhcdhLDwOY/HG4/D0YyEiIiLPYMgu8dLqf8qUKUVul3b/ISGuGdsjZfnegMdhLDwOY+FxGIs3HUdGRoa7d4OIiAzCZrO5exfIi39/5QrYZXC9v78/EhMTC9wuX8fExBT7GLm9PPcXUnLvXEYvGfaGDRti2LBhqsyiMiQrIh+2hg4d6tHrKngcxsLjMBYeh7F443FkZma6e3eIiMjN9Pc0OYkbHBzs7t2hCtJPwsvvU0r7PTpgDwgIQLdu3bB48WLV6V3IQcnXkyZNKvYxMvhevv/www87bpMPPHJ7SQIDA9VWmLyIrvqw58rnciceh7HwOIyFx2Es3nQcubm57t4NIiJyM0lkRkVFOaZlSSWwn59fuZ5DYinpE5aVleXRa7898ThsNpsK1uX3J79H+X16fMAuJPM9fvx4dO/eHT179sSbb76J9PR0R9f4cePGoX79+qqsXTz00EMYOHAg3njjDVx55ZX45ptvsH79enz44YeuPxoiIiIiIqJqolcNlzTiuixBo1RtSYa+vMG+kXjycURFRZVa/e1xAfvo0aNx+vRpPPfcc6pxXOfOnTF//nxHY7n4+PgCZ1X69u2L2bNn45lnnsHTTz+NFi1aqA7x7du3d+2REBERERERVSMJTmNjY1GnTp0KNSSVxyxfvhwDBgzw6Co0Tz0Oi8WiMute13ROyt9LKoFfunRpkdtuvPFGtREREREREXkbCfoqEvjJY2SZVVBQkEcFut56HEbkGQsMiIiIiIiIiHwMA3YiIiIiIiIiA2LATkREREREROQta9jdNcxe5rG7oiGCtO+X5/Lk9RU8DmPhcRgLj8NYvPE49Dns+vsTVR7f64vicRgLj8NYeBzG4q3HkWJ/T3Ln+71HBOypqanqsmHDhu7eFSIiogLvT5GRke7eDa/A93oiIjKqVDe+3/vZPCA9IAPsT5w4gfDw8ErP9ZOzJPJh4OjRo4iIiICn4nEYC4/DWHgcxuKNxyHvR/LmXa9evQKjTKni+F5fFI/DWHgcxsLjMBZvPQ6bzeb293uPyLDLi9OgQQOXPqf8Ajz5j0nH4zAWHoex8DiMxduOg5l11+J7fcl4HMbC4zAWHoexeONxRLr5/Z5pASIiIiIiIiIDYsBOREREREREZEA+F7AHBgbi+eefV5eejMdhLDwOY+FxGAuPg6qbt/yueBzGwuMwFh6HsfA4fLzpHBEREREREZGv8bkMOxEREREREZEnYMBOREREREREZEAM2ImIiIiIiIgMiAE7ERERERERkQH5VMD+7rvvIi4uDkFBQejVqxfWrl1bbT976tSp6NGjB8LDw1GnTh1cc8012LNnT4H7ZGVl4YEHHkCtWrUQFhaG66+/HomJiQXuEx8fjyuvvBIhISHqeR5//HHk5uYWuM/SpUvRtWtX1d2wefPmmDVrVpW9Fq+99hr8/Pzw8MMPe9xxHD9+HLfeeqvaz+DgYHTo0AHr1693fF/6MT733HOIjY1V3x8yZAj27dtX4DnOnTuHsWPHIiIiAlFRUbjzzjuRlpZW4D5bt25F//791T42bNgQ//nPf4rsy3fffYfWrVur+8h+zJs3r0zHkJeXh2effRZNmjRR+9isWTO89NJLat+NfBzLly/HyJEjUa9ePfX38/PPPxf4vpH2ubR9Ke04rFYrnnjiCfWcoaGh6j7jxo3DiRMnPOo4Crv33nvVfd58802PPI5du3Zh1KhRiIyMVL8X+XdZ/j3ytH+/qHTuem35Xm+84+B7vfuOg+/1nnMchfG9Hob498v5BfEJ33zzjS0gIMA2c+ZM244dO2x33XWXLSoqypaYmFgtP3/48OG2Tz/91LZ9+3bb5s2bbSNGjLA1atTIlpaW5rjPvffea2vYsKFt8eLFtvXr19t69+5t69u3r+P7ubm5tvbt29uGDBli27Rpk23evHm26Oho21NPPeW4z8GDB20hISG2yZMn23bu3Gn73//+Z/P397fNnz/f5a/F2rVrbXFxcbaOHTvaHnroIY86jnPnztkaN25su/32221r1qxRP++PP/6w7d+/33Gf1157zRYZGWn7+eefbVu2bLGNGjXK1qRJE1tmZqbjPpdffrmtU6dOttWrV9v++usvW/PmzW1jxoxxfD85OdlWt25d29ixY9Xv/uuvv7YFBwfbPvjgA8d9/v77b3Vs//nPf9SxPvPMMzaLxWLbtm3bRY/jlVdesdWqVcv222+/2Q4dOmT77rvvbGFhYba33nrL0Mchv/N//etfth9//FE+bdh++umnAt830j6Xti+lHUdSUpL6G58zZ45t9+7dtlWrVtl69uxp69atW4FjNfpxOJPvy77Wq1fPNn36dI87Dvn/u2bNmrbHH3/ctnHjRvX1L7/8UuDfDE/494tK587Xlu/1xjoOvtfzvb6s+8z3+gv4Xm8zxL9fznwmYJf/eR544AHH13l5eeoPcerUqW7Zn1OnTqk/smXLljn+h5c/VvlHWLdr1y51H/mf///bu/cQqco/juPfXTezKEnTXNA0gtVsjTSF2AwNNtSlP7ZdcEvKzILKECOkDKLsKnSlC2VkN8GVksi0LHLTtRRNupB2ge2mpqGUoa2hudaeH59vnOHMujtNv9ad5zjvF4yzM3M883zPzHm+5zzznOcRfVlKS0ujPXv2ZJZZuHBh1Ldv3+jw4cP++Pbbb48qKyuz3uuKK67wg4ju3BYHDhyIKioqoqampmjixImZJJ6WOObNmxddfPHFXb7e3t4elZeXR4888kjmOcV24okneuUj2jkV18cff5xZ5t13341KSkqin376yR8/++yzUb9+/TJxxe89YsSIzOOGhobosssuy3r/Cy+8MLrxxhv/MQ79v+uuuy7rufr6eq8o0xJHx8o2pDLnU5au4ujqwFfL7dixI3Vx7Nq1Kxo8eLAnYB0AJ5N4WuJQHXL11VcfFVtyXWmov5BbSNuWXE+uj5HryfVpiINcvymY+iupKLrEt7W12aeffurdKWKlpaX+eNOmTQUp02+//eb3/fv393uVT91qkmVUV5GhQ4dmyqh7dRsZNGhQZpnJkydba2urffXVV5llkuuIl4nX0V3bQt1I1E2k43ulJY6VK1fauHHjbOrUqd7NZcyYMbZo0aLM69u2bbM9e/ZkrV/datSVJRmHugNpPTEtr3Js3rw5s8yECROsd+/eWXGoi+S+ffvyijWXiy66yNasWWPffPONP96yZYtt2LDBampqUhVHUkhlzqcs/3a/V/ctlT1NcbS3t9v06dO9O1hlZeVRr6chDsWwatUqGz58uK9T+73+X7IrXVrqL3QttG1LrifXk+s7F1KZyfV/I9cPDar+SiqKE/a9e/f69T/JjS56rC9ET9OXSdeBjR8/3kaNGuXPqRz6csc7d2dl1H1nMcSv5VpGX7BDhw51y7Z49dVX7bPPPvNr9TpKSxw//PCDLVy40CoqKuy9996zWbNm2Zw5c2zx4sVZ5ci1ft2rIkgqKyvzA7PuiDWfOO644w678sorvaI54YQT/GBE3y1dX5SmOJJCKnM+ZcmXrpfSdW7Tpk3za7/SFMdDDz3k5dI+0pk0xPHzzz/7dXa6FnfKlCm2evVqq6urs/r6evvggw9SVX+hayFtW3J94eMg14cVR1JIZSbX/41cPyio+iup7F8tjW6hFusvv/zSW0fTZufOnXbLLbdYU1OTD56QVjqQUgvhggUL/LGSnz6T5557zmbMmGFpsWzZMmtsbLSlS5d6a+jnn3/uSVwDcaQpjuOdWnIbGhp8EBUdPKaJWoeffPJJP3DXLwZp3ueltrbWbr31Vv979OjRtnHjRt/vJ06cWOAS4nhDri88cj16Erm+8NqP01xfFL+wDxgwwHr16nXU6H96XF5e3qNlmT17tr399tvW3NxsQ4YMyTyvcqjrxP79+7sso+47iyF+LdcyauXTKIv/dVtoh1brlUZEVKuabmqxeuqpp/xvtRqlIQ6NOnnuuedmPTdy5MjMCJLxOnKtX/faFkkaPVIjaHZHrPnEoW5Lccu7uu6oK5MqqPgXkbTEkRRSmfMpS74JfMeOHX7wG7e4pyWO9evXexnVVSze5xXL3LlzfdTTtMShOkNl/6f9Pg31F7oWyrYl14cRB7k+rDiSQiozuZ5cH2L9VXQn7Or2MHbsWL/+J9kCo8dVVVU9Uga1timBL1++3NauXetTcySpfOrmlCyjrvfQlysuo+6/+OKLrJ0lrhTiL6aWSa4jXiZex3/dFtXV1V4Gte7GN7Veq1tW/Hca4lAXxY5T7ejasGHDhvnf+ny0MyXXry4uukYnGYd2dh3YxPTZqhy6XiZeRtNPqCJPxjFixAjr169fXrHmcvDgQb8eJkmVQ9zCmJY4kkIqcz5lySeBayqS999/36cPSUpDHDow1BQtyX1ev+roAFJdTNMSh+oMTeuSa79PSz2MrhV625Lrw4qDXB9WHEkhlZlcT65vCbD+yhIVCQ2rr9EFX3nlFR/l8IYbbvBh9ZOj/x1Ls2bN8ukJ1q1bF+3evTtzO3jwYNYUA5r+Ze3atT7FQFVVld86TjEwadIkny5G0wYMHDiw0ykGNJWBRjx85plnOp1ioDu3RXLk2LTEoRE8y8rKfKqUb7/9NmpsbPT3W7JkSdaUElqfpoLYunVrVFtb2+l0I2PGjPHpYjZs2OCj6Sant9BIlJreYvr06T7ipsqs9+k4vYXK8uijj3qs8+fPz3uqlxkzZvhonvFUL5riQtNOaOTKkOPQyMOaJkM3VUOPP/64/x2PqBpSmXOVJVccbW1tPg3JkCFD/Hue3O+To6eGHkdnOo4cm5Y4tH9ofc8//7zv9/EULJqaJk31F3Ir5LYl14cVB7meXJ9vmcn1RyPXjwqmHi6aE3bRB6YPR/PhaZh9zSHYU/SF6uym+Vpj+hLefPPNPh2CvgB1dXW+wydt3749qqmp8TkNVVnPnTs3OnLkSNYyzc3N0ejRoz3Os88+O+s9jsW26JjE0xLHW2+95TujdqRzzjnHd+wkTStx1113ecWjZaqrq6OWlpasZX799VevqDQfqqZ6mDlzplckSZo/UtPKaB1KuKqEOlq2bFk0fPhwj0NTRKxatSqvGFpbW33baxv06dPHt5PmpkwmiRDj0Gfb2f6gg5LQypyrLLni0EFVV/u9/l9a4sg3iacljhdffNHnjdX+orlkNcdrUlrqL+RWqG1Lrg8vDnJ94eIg15PryfXdUw+X6J9/95s8AAAAAAA41oriGnYAAAAAANKGE3YAAAAAAALECTsAAAAAAAHihB0AAAAAgABxwg4AAAAAQIA4YQcAAAAAIECcsAMAAAAAECBO2AEAAAAACBAn7MBx6Nprr7XLL7+80MUAAADHCLkeKA6csAMAAAAAECBO2IEUe/311+28886zk046yU4//XS79NJL7bbbbrPFixfbihUrrKSkxG/r1q3z5Xfu3GkNDQ122mmnWf/+/a22tta2b99+VGv9vffeawMHDrS+ffvaTTfdZG1tbQWMEgCA4kWuB4pbWaELAOD/s3v3bps2bZo9/PDDVldXZwcOHLD169fbNddcYz/++KO1trbayy+/7MsqYR85csQmT55sVVVVvlxZWZk98MADNmXKFNu6dav17t3bl12zZo316dPHE78S/MyZM/0A4cEHHyxwxAAAFBdyPQBO2IEUJ/E///zT6uvrbdiwYf6cWuBFrfCHDx+28vLyzPJLliyx9vZ2e+GFF7wlXpTk1QKvhD1p0iR/Tsn8pZdespNPPtkqKyvtvvvu85b8+++/30pL6ZQDAEBPIdcDYI8EUur888+36upqT9xTp061RYsW2b59+7pcfsuWLfbdd9/Zqaeeaqeccorf1Br/xx9/2Pfff5+1XiXwmFrpf//9d+9iBwAAeg65HgC/sAMp1atXL2tqarKNGzfa6tWr7emnn7Y777zTNm/e3OnySsRjx461xsbGo17TNWwAACAs5HoAnLADKabubuPHj/fb3Xff7d3lli9f7l3d/vrrr6xlL7jgAnvttdfsjDPO8AFmcrXOHzp0yLvayUcffeQt9GeeeeYxjwcAAGQj1wPFjS7xQEqpdX3BggX2ySef+MAzb7zxhv3yyy82cuRIO+uss3xwmZaWFtu7d68PQnPVVVfZgAEDfLRYDUSzbds2v55tzpw5tmvXrsx6NUrs9ddfb19//bW98847Nn/+fJs9ezbXtAEA0MPI9QD4hR1IKbWcf/jhh/bEE0/4KLFqcX/sscespqbGxo0b5wla9+oe19zcbJdccokvP2/ePB+8RiPNDh482K+NS7bC63FFRYVNmDDBB7PR6LT33HNPQWMFAKAYkesBlERRFBW6EADCoLlZ9+/fb2+++WahiwIAAI4Bcj2QLvR7AQAAAAAgQJywAwAAAAAQILrEAwAAAAAQIH5hBwAAAAAgQJywAwAAAAAQIE7YAQAAAAAIECfsAAAAAAAEiBN2AAAAAAACxAk7AAAAAAAB4oQdAAAAAIAAccIOAAAAAECAOGEHAAAAAMDC8z+/+QaIJPZrIgAAAABJRU5ErkJggg=="
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T02:26:54.169169Z",
     "start_time": "2025-01-17T02:26:52.884884Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "model=model.to(device)\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/bn/best.ckpt\",weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3097\n",
      "accuracy: 0.8929\n"
     ]
    }
   ],
   "execution_count": 18
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
